The program for reading data from the com port. Redirecting data from a COM port to the Web

  • Analyze serial port activity

    Serial Port Monitor can connect to a COM port, even if it is already open by some application, and immediately start monitoring it. All data passing through the monitored COM port will be displayed in our monitoring program. Since everything is recorded in real time, you can spot problems instantly. For data comparison, there is a function of synchronized allocation of the same IRP in different views.

    In addition, you can redirect all monitoring data to a specified file or copy all recorded data to the clipboard. Serial Port Monitor gives you the ability to capture and record all serial port I / O control codes (IOCTLs), monitor all their data and parameters. You can save any monitoring session and, if necessary, load it next time.

  • Monitor multiple ports within one session

    Serial Port Monitor has a unique functionality monitoring multiple COM ports at the same time. Now you can collect data on how applications interact with two or more ports and, in parallel, with several devices within the same session. Received and sent monitoring data will be presented (recorded) in a separate log in the order of receipt, which will greatly simplify the analysis.

  • Different options for viewing the received data

    You can view monitoring data in 4 modes at once: table, line, dump or terminal, each of which offers its own way of representing the recorded serial data. Serial Port Monitor allows you to select monitoring filters, thus saving you time and allowing you to track only events of interest. In the settings you can select the data for display: binary, ASCII, configure the port. Any settings for displaying can be applied directly in the current monitoring process.

  • Emulate data transmission to a serial device

    You can send data in various formats (string, binary, octal, decimal, hexadecimal, mixed) to the monitored serial port as if it were sent directly by the monitored application using the Serial Port Monitor terminal mode feature. In this way, you can monitor the reactions of the monitored serial device to certain special commands and data.

  • Full support for Modbus data protocol (RTU and ASCII)

    With the new Serial Port Monitor filters you can decrypt and analyze Modbus data. The program will help not only establish a connection between RS485 / 422/232 devices, but also carry out an effective analysis of passing data.

  • Replaying and comparing monitoring sessions

    Serial Port Monitor provides a unique opportunity to replay the session from the application to the port for the best analysis of ongoing processes. You will be able to observe the reaction of the serial port to the passage of the same data, thereby increasing the efficiency of monitoring. You also have the ability to compare multiple monitoring sessions and automatically track the difference between them.

So we got to the COM port. But with it everything is not as simple as with LPT, and its full use will require much more effort. The main snag is its main advantage - serial data transfer. If in LPT the data byte is transmitted over 8 lines, one bit per each, and the state of each line could be easily viewed, then in the COM port the data byte is transmitted bit by bit along one line (relative to the ground, of course) and see what is transmitted there from LEDs alone will not work. To do this, you need a special device - a serial to parallel data stream converter, the so-called. USART (Universal Synchronous / Asynchronous Receiver Transmitter). For example, it is in the composition motherboard a computer equipped with a COM port, in any more serious microcontroller.


I hope you are still lost in the development of the COM port. It's not all that gloomy. Some results can be obtained without the USART. Let's formulate the task that we will implement at the initial stage of working with the COM port:


"I want an LED to be connected to the computer via the COM port. I launch the program. I give some action in this program, the LED lights up, I do something else - the LED goes out."


The task is quite specific (taking into account the fact that USART is not used) and is pure "self-footed", but it is quite realizable and workable. Let's get down to implementing it.


1. COM port

We take again system unit your PC and look at the rear. We note the 9-pin connector there - this is the COM port. In reality, there may be a few (up to 4). There are two COM ports installed on my PC (see photo).


2. Extension COM port


3. Hardware

We also have to "tinker" with the hardware, in the sense that it will be more complicated than with the first device for the LPT port. The fact is that the RS-232 protocol through which data is exchanged in the COM port has a slightly different logical state - voltage ratio. If usually it is logical 0 0 V, logical 1 +5 V, then in RS-232 this relationship is as follows: logical 0 +12 V, logical 1 -12 V.

And for example, having received -12 V, it is not immediately clear what to do with this voltage. Usually, the conversion of RS-232 levels to TTL (0.5 V) is carried out. The simplest option is zener diodes. But I propose to make this converter on a special microcircuit. It is called MAX232.

Now let's see what signals from the COM port can we see on the LEDs? In fact, there are as many as 6 independent lines in the COM port, which are of interest to the interface device designer. Two of them are not yet available to us - lines for transferring serial data. But the remaining 4 are designed to control and indicate the data transfer process and we can "transfer" them to our needs. Two of them are intended for control from the side of an external device and we will not touch them for now, but we will now use the last two remaining lines. They're called:

  • RTS- Request for transfer. Interaction line that indicates that the computer is ready to receive data.
  • DTR- The computer is ready. Interaction line, which indicates that the computer is turned on and ready to communicate.

Now we will transfer their purpose a little, and the LEDs connected to them will either go out or light up, depending on the actions in our own program.

So, let's put together a diagram that will allow us to carry out the intended actions.

And here is its practical implementation. I think you will forgive me that I made it in such a dumb prototype version, because I don’t want to make a board for such a "highly productive" circuit.


4. Software part

Everything is simpler here. Let's create Windows application in Microsoft Visual C ++ 6.0 based on MFC to control two lines of COM port communication. For this we create new project MFC and give it a name, for example, TestCOM... Next, we select the construction option based on the dialogue.

Attach appearance the dialog window of our program, as in Fig. below, namely add four buttons, two on each of the lines. One of them, respectively, is necessary in order to "extinguish" the line, the other to "set" it to a unit.

Class CTestCOMDlg: public CDialog (// Construction public: CTestCOMDlg (CWnd * pParent = NULL); // standard constructor HANDLE hFile;

In order for our program to be able to control the lines of the COM port, it must first be opened. Let's write the code responsible for opening the port when loading the program.

HFile = CreateFile ("COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) (MessageBox ("Failed to open port!", "Error", MB_ICONERROR);) else (MessageBox ("Port opened successfully", "Ok", MB_OK);)

Using a standard Win API function CreateFile () open the COM port COM2... Next, we check the success of the opening with the output of an informational message. Here you need to make an important note: COM2 is in my computer, but on your computer you could connect it to another COM port. Accordingly, its name must be changed to whatever port you are using. You can see what port numbers are present on your computer like this: Start -> Settings -> Control Panel -> System -> Hardware -> Device Manager -> Ports (COM and LPT).

As a result, the function CTestCOMDlg :: OnInitDialog () located in the file TestCOMDlg.cpp, the class of our dialog should take the form:

BOOL CTestCOMDlg :: OnInitDialog () (CDialog :: OnInitDialog (); // Add "About ..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT ((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT (IDM_ABOUTBOX AppendMenu (MF_SEPARATOR); pSysMenu-> AppendMenu (MF_STRING, IDM_ABOUTBOX, strAboutMenu);)) // Set the icon for this dialog. The framework does this automatically // when the application "s main window is not a dialog SetIcon (m_hIcon, TRUE); // Set big icon SetIcon (m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here hFile = CreateFile ("COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) (MessageBox ("Failed to open port!", "Error", MB_ICONERROR);) else (MessageBox ("Port successfully opened", "Ok", MB_OK);) return TRUE; // return TRUE unless you set the focus to a control)

Now let's add handlers for line control buttons. I gave them appropriate names: the function that sets one on the DTR line is OnDTR1 (), 0 is OnDTR0 (). For the RTS line, respectively, in the same way. Let me remind you that the handler is created when you double-click on the button. As a result, these four functions should take the form:

Void CTestCOMDlg :: OnDTR1 () (// TODO: Add your control notification handler code here EscapeCommFunction (hFile, 6);) void CTestCOMDlg :: OnDTR0 () (// TODO: Add your control notification handler code here EscapeCommFunction (hFile, 5);) void CTestCOMDlg :: OnRTS1 () (// TODO: Add your control notification handler code here EscapeCommFunction (hFile, 4);) void CTestCOMDlg :: OnRTS0 () (// TODO: Add your control notification handler code here EscapeCommFunction (hFile, 3);)

Let me explain a little how they work. As you can see, inside they contain a call to the same Win API function EscapeCommFunction () with two parameters. The first one is the HANDLE on open port, the second is a special action code corresponding to the required line state.

That's it, let's compile it, launch it. If all is well, you should see a message that the port was successfully opened. Then, by pressing the corresponding buttons, we blink the LEDs connected to the COM port.

© Dmitry Ivanov
December 2006

How to beautifully represent the data sent by Arduin to Serial. In my opinion, the guys offered a very beautiful solution, which, on the one hand, looks quite simple, and on the other hand, allows you to get an excellent result with a minimum of effort.

In the comments to the article, it was expressed regret that such a solution would not work under Firefox, and the idea was expressed that "you can still write a simple web server with html output based on this thing." I was hooked by this idea, a quick search on google did not give a ready-made solution, and I decided to implement the idea myself. And that's what came of it.

Warning! The proposed solution should by no means be considered complete. Unlike Serial Projector from Amperka, it is a concept, a demonstration of a possible approach, a working prototype and nothing more.

Some time ago I did a project in which I used the accelerometers built into an Android smartphone to control the servos connected to the Arduino. Then for these purposes I used the Scripting Layer for Android (SL4A) and RemoteSensors projects. It turns out that the baseHTTPServer package is included in the python standard library, with the help of which it is a task for a couple of lines of code to raise a web service in python.

There were no sensors for the Arduino at hand, so I used the internal thermometer built into the Arduino Uno as the source of the displayed information. As far as I understand, it is not very accurate and is not intended to measure temperature at all environment, but for prototyping it will do just fine.

After a short googling, the following sketch for Arduinka appeared:

// source: https://code.google.com/p/tinkerit/wiki/SecretThermometer long readTemp () (long result; // Read temperature sensor against 1.1V reference ADMUX = _BV (REFS1) | _BV (REFS0) | _BV (MUX3); delay (2); // Wait for Vref to settle ADCSRA | = _BV (ADSC); // Convert while (bit_is_set (ADCSRA, ADSC)); result = ADCL; result | = ADCH<<8; result = (result - 125) * 1075; return result; } void setup() { Serial.begin(115200); } int count = 0; void loop() { String s = String(count++, DEC) + ": " + String(readTemp(), DEC); Serial.println(s) delay(1000); }
This sketch opens a COM port, sets it to 115200 baud, and then writes the current value of the built-in thermometer to it every second. (Do not ask me in what units the temperature is given - for the described task it does not matter). Since the value is not changing very actively, the line number is displayed in front of the temperature for better visibility of the data change.

To check that the web server will send out only whole lines, and not parts of them as it reads from the COM port, the line
Serial.println (s)
was replaced by
for (int i = 0; i< s.length(); i++){ Serial.print(s.charAt(i)); delay(200); } Serial.println("");
those. the generated string is output to the serial port not in its entirety, but character by character, with pauses of 200 ms.

To begin with, a very simple prototype of a web server was written (below it is disassembled in parts):
# - * - coding: utf-8 - * - # - based on: https://raw.githubusercontent.com/Jonty/RemoteSensors/master/remoteSensors.py SERIAL_PORT_NAME = "COM6" SERIAL_PORT_SPEED = 115200 WEB_SERVER_PORT = 8000 import time , BaseHTTPServer, urlparse import serial ser = None def main (): global ser httpd = BaseHTTPServer.HTTPServer (("", WEB_SERVER_PORT), Handler) # - workaround for getting IP address at which serving import socket s = socket.socket ( socket.AF_INET, socket.SOCK_DGRAM) s.connect (("google.co.uk", 80)) sData = s.getsockname () print "Serving at"% s:% s ""% (sData, WEB_SERVER_PORT) ser = serial.Serial (SERIAL_PORT_NAME, SERIAL_PORT_SPEED, timeout = 0) httpd.serve_forever () class Handler (BaseHTTPServer.BaseHTTPRequestHandler): # Disable logging DNS lookups def address_string (self): return str (self.client_address) def do_GET self.send_response (200) self.send_header ("Content-type", "application / x-javascript; charset = utf-8") self.end_headers () try: while True: new_serial_line = get_full_line_from_serial () if new_serial_line is not None: self.wfile.write (new_serial_line) self.wfile.write ("\ n") self.wfile.flush () except socket.error, e: print "Client disconnected. \ n" captured = "" def get_full_line_from_serial (): "" "returns full line from serial or None Uses global variables" ser "and" captured "" "" global captured part = ser.readline () if part: captured + = part parts = captured.split ("\ n", 1); if len (parts) == 2: captured = parts return parts return None if __name__ == "__main__": main ()
Let's analyze the script piece by piece.

Since this is a prototype, all the main parameters of work (the name of the COM port, its speed, as well as the number of the TCP port on which the web server will run) are indicated directly in the source text:
SERIAL_PORT_NAME = "COM6" SERIAL_PORT_SPEED = 115200 WEB_SERVER_PORT = 8000
Of course, you can arrange to read these parameters from the command line. For example, the argparse module makes this very fast, simple and flexible.

In this case, Windows users need to find out the name of the COM port to which the Arduin is connected in the device manager. I had it "COM6". Users of other operating systems need to use the tools of their OS. I have no experience with MacOS at all and in Linux I did not work with COM ports either, but there, most likely, it will be something like "/ dev / ttySn".

Next is the definition of a global variable to which an instance of the Serial class will be bound, which is responsible in Python for working with the COM port:
ser = None
In line
httpd = BaseHTTPServer.HTTPServer (("", WEB_SERVER_PORT), Handler)
a web server is created that will listen to requests on the specified WEB_SERVER_PORT. And these requests will be handled by an instance of the Handler class, described below.

The following lines are a small "hack" that allows you to display the IP address on which the running web server is actually running:
# - workaround for getting IP address at which serving import socket s = socket.socket (socket.AF_INET, socket.SOCK_DGRAM) s.connect (("google.co.uk", 80)) sData = s.getsockname () print "Serving at"% s:% s ""% (sData, WEB_SERVER_PORT)
As far as I understood, there is no other way to find out this IP. And without this knowledge, how will we contact our server from the browser?

Therefore, you have to open a socket and connect to the Google site in order to extract information about your own IP address from the attributes of this socket.

Below is the opening of the COM port and the actual launch of the web server:
ser = serial.Serial (SERIAL_PORT_NAME, SERIAL_PORT_SPEED, timeout = 0) httpd.serve_forever ()
This is followed by a description of the class that is responsible for processing the requests received by the running web server:
class Handler (BaseHTTPServer.BaseHTTPRequestHandler):
This is a heir of the class built into the BaseHTTPServer module, in which it is enough to override only the do_GET method

Since this is still a prototype, the server will be "glad" to any request - whatever URL is requested from it, it will give the client all the data read from the COM port. Therefore, in Handler.do_GET, it immediately responds with a success code and the necessary headers:
self.send_response (200) self.send_header ("Content-type", "application / x-javascript; charset = utf-8") self.end_headers ()
after which an endless loop starts, in which an attempt is made to read a whole line from the COM port and, if this attempt was successful, transfer it to the web client:
while True: new_serial_line = get_full_line_from_serial () if new_serial_line is not None: self.wfile.write (new_serial_line) self.wfile.write ("\ n") self.wfile.flush ()
In the project that was taken as a basis, this endless loop was "wrapped" in a try ... except block, with the help of which it was supposed to carefully handle the connection break. Perhaps, in Android (the base project was developed for it) this works fine, but it did not work out for me under Windows XP - when the connection was broken, some other exception occurred, which I never learned to intercept. Fortunately, this did not prevent the web server from working properly and accepting further requests.

The function of receiving a whole line from a COM port works on the same principle as that of the creators of Serial Projector:

  • there is some global buffer that stores everything that is read from the COM port
  • every time the function is called, it tries to read something from the COM port
  • if she succeeds, then
    • it adds what it just read to the specified global buffer
    • tries to split the global buffer into at most two parts with an end-of-line character
    • if it succeeds, then it returns the first part to the calling procedure, and uses the second part as the new value of the global buffer
  • if there is no new data in the COM port or the end-of-line character is not found, the function returns None:
captured = "" def get_full_line_from_serial (): "" "returns full line from serial or None Uses global variables" ser "and" captured "" "" global captured part = ser.readline () if part: captured + = part parts = captured.split ("\ n", 1); if len (parts) == 2: captured = parts return parts return None
As a result, it turned out like this:

It can be seen that lines read from the COM port appear in the browser. I don't understand anything about the web front-end: JavaScript, Ajax, CSS and DOM are a dark forest for me. But it seems to me that for programmers who create web interfaces, this should be enough to convert this output into the same beautiful image that the Serial Projector from Amperka produces. In my opinion, the task boils down to creating a javascript script that accesses the web server, reads a stream from it and outputs the last line read to the right place on the web page.

Just in case, I decided to play it safe and tried to make the first approximation on my own. A not very deep search in Google suggested that, in fact, for such purposes, at least, they used WebSockets or Server-Sent Events before. I found what I thought was a good one for using Server-Sent Events and decided to use this technology.

Note! It looks like this is not the best solution, because this technology did not work either in Internet Explorer 8 or in the browser built into Android 2.3.5. But it worked at least in Firefox 39.0, so I didn't dig further.

From the point of view of the Python script, the changes under Server-Sent Events are absolutely insignificant:

  • it is necessary to replace the type of data given to the client:
    line
    self.send_header ("Content-type", "application / x-javascript; charset = utf-8")
    replaced by
    self.send_header ("Content-type", "text / event-stream")
  • and also insert the "data:" prefix before the line read from the COM port and add one more line feed character:
    strings
    self.wfile.write (new_serial_line) self.wfile.write ("\ n")
    replaced by
    self.wfile.write ("data:" + new_serial_line) self.wfile.write ("\ n \ n")

Everything else could probably remain unchanged, but ...

First, I created an index.html file with the following content:

Heading


The most interesting in it is the line
which forms a place for the output of the next line from the COM port, and a javascript script

which actually reads the stream from the web server and outputs the read information to the specified location.

I was supposed to open this file in a browser, for example, from a disk or from some other web server, but it did not work: when opening a page from disk, the javascript script once accessed the running Python web server and immediately terminated the connection. I did not understand why this is happening, and suggested that this is perhaps some kind of manifestation of the browser's protection against various attacks. He probably doesn't like the fact that the page itself is opened from one source, and the script reads data from another source.

Therefore, it was decided to change the Python web server so that it also serves this html page. Then it would turn out that both the page and the stream are read from the same source. I don't know if my assumption about security turned out to be correct, or something else, but with this implementation, everything worked as it should.

Of course, you only need to change the Handler request handler class:
class Handler (BaseHTTPServer.BaseHTTPRequestHandler): # Disable logging DNS lookups def address_string (self): return str (self.client_address) def do_GET (self): if self.path == "/" or self.path == "/ index .html ": self.process_index () elif self.path ==" / get_serial ": self.process_get_serial () else: self.process_unknown () def process_index (self): self.send_response (200) self.send_header (" Content -type "," text / html; charset = utf-8 ") self.end_headers () self.wfile.write (open (" index.html "). read ()) self.wfile.write (" \ n \ n ") self.wfile.flush () def process_get_serial (self): self.send_response (200) self.send_header (" Content-type "," text / event-stream ") self.end_headers () try: while True: new_serial_line = get_full_line_from_serial () if new_serial_line is not None: self.wfile.write ("data:" + new_serial_line) self.wfile.write ("\ n \ n") self.wfile.flush () except socket.error, e : print "Client disconnected. \ n" def process_unknown (self): self.send_response (404)
In this case, it is assumed that the web server will only respond to two requests: "/index.html" (giving the html-code of the page) and "/ get_serial" (giving an endless stream of lines read from the COM-port). It will respond to all other requests with a 404 code.

Since index.html is served by the Python web server, it can be slightly modified by specifying the relative one instead of the absolute address of the stream of strings from the COM port:
string
var source = new EventSource ("http://192.168.1.207:8000/")
replaced by
var source = new EventSource ("/ get_serial")
In the end, it turned out like this:

On this I decided to stop. It seems to me that it should be quite simple to design the page beautifully. But I don't know HTML or CSS, so let someone else do it. I saw my task in showing that making a web service that sends data from a COM port seems to be not difficult at all.

I repeat once again: the presented code is not a complete solution that can be "put into production". This is just a prototype that shows a principled approach to solving the problem.

What else can you work on here:

  • firstly, reading data from a COM port in a Python script is done very "clumsily" - in fact, there is a constant polling "is there anything fresh?" This approach, of course, loads the processor and one core on my computer is 100% busy.
    As a solution, you can use a blocking read with a timeout. To do this, it is enough to specify a non-zero value (in seconds) as the timeout when opening the COM port, for example:
    ser = serial.Serial (SERIAL_PORT_NAME, SERIAL_PORT_SPEED, timeout = 0.03)
    In addition, in the description of the pySerial module there are three examples of creating a bridge: "TCP / IP - serial bridge", "Single-port TCP / IP - serial bridge (RFC 2217)" and "Multi-port TCP / IP - serial bridge (RFC 2217) "- you can see how professionals solve such problems.
  • secondly, only one client can receive data. Until the page is closed on the first client, you cannot connect to this server and get the values ​​on the second computer. On the one hand, this is probably correct: there is only one COM port, and there are several consumers - which of them should give the line they read? If you think that the answer to this question should be “everyone”, then here are my thoughts on the matter. It seems to me that the issue cannot be solved only by using an "honest" multithreaded web server (for example, some Tornado or Flask), which can simultaneously serve requests from several web clients. Because you cannot open a COM port from each thread and read from it - in this case, data from the COM port will only go to one thread / process. Therefore, in my opinion, you need to split the server side into two parts:
    • zmq server that works with a COM port, reads lines from it and sends them through a PUB socket to all interested consumers
    • python web server, instead of connecting to a COM port, connects to the zmq server and receives data from it
    If you are not familiar with the ZMQ (ZeroMQ) library, then you can use ordinary TCP / IP or UDP sockets instead, but I would highly recommend getting to know ZMQ, because this library greatly facilitates the solution of such tasks. It seems to me that with the help of ZMQ, the solution will fit within a maximum of 20 lines. (I can not resist not to write: even if you do not plan to solve the described problem, but your work is related to multi-threaded / multi-process programming with the exchange of data between threads / processes, take a closer look at this library - perhaps this is what you have been talking about for so long dreamed)
  • data flow is still unidirectional - from the COM port to the web browser. You cannot send data to the Arduino from the browser yet. It seems to me that this task is also not very difficult and, unlike the previous one, it can only be solved
    • using a multi-threaded server
    • finalizing the Handler.do_GET method so that it accepts GET requests with parameters and sends the values ​​of certain of them to the COM port
    In my opinion, if you want to write a full-fledged analogue of the built-in serial port monitor in the Arduino IDE based on web technologies is not so difficult. For myself, I see the difficulty only in creating a normal frontend.
  • it is not yet possible to set the name of the COM port and its operation parameters through the browser. On the one hand, it seems logical: how can a user on the other side of our planet know which COM port and at what speed the arduin is connected? But the Python web server running on the same computer knows for sure. But if it is still desirable to give the user the ability to change the name of the COM port or the parameters of its operation, then again this can be easily solved by modifying the Handler.do_GET method
  • you need to install python to run the server. In general, this is not difficult, but if for some reason it is impossible or does not want to do this, then pyInstaller can come to the rescue. With its help, the Python script can be compiled into one executable file (in the case of Windows - in.exe), which can be simply copied to the computer to which the arduinka is connected.
    Perhaps the best solution would be to use the Go language in this case. As far as I know, it solves the problem of creating a file for "distribution" better.
In conclusion: the question may arise: "Isn't it easier to solve this problem through some ready-made cloud?" Why not publish the data read from the COM port in the cloud, and on the clients just access the corresponding service in the cloud? Probably, such a solution also has a right to exist, but before applying such a solution, the following questions must be answered:
  • are there out-of-the-box web services that allow me to publish data at the speed / frequency I need? Are there free ones among them or are you willing to pay the corresponding money?
  • are you ready for the fact that in the event of a cloud fall or a connection to it, you will be left without data
  • Does it bother you that in order to transfer data from one room to another, they will cross the ocean or half a continent twice?

Serial ports are loved by developers for their ease of maintenance and use.

And of course, writing to the console of the terminal program is all good, but I want my own application, which, by pressing a key on the screen, performs the actions you need;)

In this article I will describe how to work with com port in C ++ language.

The solution is simple, but for some reason a working example was not found immediately. For the sim I save it here.

Of course, you can use cross-platform solutions like QSerial - libraries included in Qt, I will probably do so, but in the future. Now we are talking about "pure" Windows C ++... We will write in Visual Studio. I have 2010, although it doesn't matter ...

Create a new Win32 console project.

Include header files:

#include #include using namespace std;

We declare the com port handler:

HANDLE hSerial;

I do it globally, so as not to bother with pointers when passing it to functions.

Int _tmain (int argc, _TCHAR * argv) (

I hate the Windows programming style. They called everything their own way and sit rejoicing ...

Now the magic of declaring a line with a port name. The fact is that it cannot convert char itself.

LPCTSTR sPortName = L "COM1";

Serial ports in Windows work like a file. We open the first com port for writing / reading:

HSerial = :: CreateFile (sPortName, GENERIC_READ | GENERIC_WRITE, 0,0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

We check the performance:

If (hSerial == INVALID_HANDLE_VALUE) (if (GetLastError () == ERROR_FILE_NOT_FOUND) (cout<< "serial port does not exist.\n"; } cout << "some other error occurred.\n"; }

Now you need to configure the connection parameters:

DCB dcbSerialParams = (0); dcbSerialParams.DCBlength = sizeof (dcbSerialParams); if (! GetCommState (hSerial, & dcbSerialParams)) (cout<< "getting state error\n"; } dcbSerialParams.BaudRate=CBR_9600; dcbSerialParams.ByteSize=8; dcbSerialParams.StopBits=ONESTOPBIT; dcbSerialParams.Parity=NOPARITY; if(!SetCommState(hSerial, &dcbSerialParams)) { cout << "error setting serial port state\n"; }

On msdn it is advised to first get the parameters and then change them. We are still learning, so we do as requested.

Now let's declare the string to be passed and the variables needed for this:

Char data = "Hello from C ++"; // string to transfer DWORD dwSize = sizeof (data); // the size of this string DWORD dwBytesWritten; // there will be the number of bytes actually transferred

We send the string. Let me remind you that the example is the simplest, so I don't do any special checks:

BOOL iRet = WriteFile (hSerial, data, dwSize, & dwBytesWritten, NULL);

I also decided to display the size of the line and the number of bytes sent for control:

Cout<< dwSize << " Bytes in string. " << dwBytesWritten << " Bytes sended. " << endl;

At the end of the program, we make an endless loop of data reading:

While (1) (ReadCOM ();) return 0; )

Now the read function:

Void ReadCOM () (DWORD iSize; char sReceivedChar; while (true) (ReadFile (hSerial, & sReceivedChar, 1, & iSize, 0); // get 1 byte if (iSize> 0) // if something is received, output cout<< sReceivedChar; } }

That's the whole example.



This library is called SerialGate.dll... If you want to use the examples of this and subsequent articles, download it (the library) and, if desired, its sources in the previous article in this section.

As a "training base", I decided to consider the solution to a rather often encountered problem - transferring data between two computers. We will transfer data, of course, through the COM port.

As strange as it may seem, let's start with the hardware. In order to transfer data between two computers, they must be connected with a special cable. It is called a "null modem" COM port cable. It is sold under the name DB9 Null Modem Cable at any radio parts store. A COM port - female connector is installed at both ends. Connect the ports of two computers with this cable.

Of course, some of the readers asked the question - I do not have two computers, how can I try to transfer data via the COM port? In this case, the following option is perfect for testing and verification - to use two COM ports of the same computer. Those. send data from the program to one port, and read data from another, having previously connected them with a null-modem cable.


Let's get down to programming. First, let's write a console application that will read a character from the keyboard and send it to the COM port. We create, as usual, a console application in Microsoft Visual Studio and name it, for example, COMServer... The SerialGate.dll library must be connected to an empty blank. The sequence of actions in this case is completely similar to working with the inpout32.dll library. Put the files from the downloaded archive SerialGate.lib, SerialGate.h to the project folder and SerialGate.dll to the folder for the executable program (Debug or Release). Next, add the lib and h file in the project itself. If you are too lazy to do all this, the project can be downloaded ready-made.

This is actually the program code itself. Consider what is happening in it. First, an instance of the SerialGate class is created - sg. Next, we try to open access to the COM port using the OpenPort () class method. To do this, you need to pass the COM port number (an integer) to the function. That is, for example, if you have a COM port called COM3 installed on your computer, and you want to open it, you need to pass the number 3 to the function as the first parameter. The second parameter is the speed at which the port will operate when transferring data. The speed for COM ports is measured in bits per second and can take some value from a certain list. By default, the following speeds can be used in Windows:

110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000 and 256000 bps.

However, many virtual COM ports support much higher speeds (up to 1 MB / s). In this case, you need to use the speeds at which this or that third-party port provides. The speeds listed above work unambiguously for any ports.

In the example shown, port "COM1" is opened at 57600 bps. Then, in an "endless loop", a character is entered from the keyboard and, using the Send () class method, is sent to the COM port. The function is passed two parameters: the address to the buffer, which contains the data to be sent, and the second parameter is the size of this data in bytes. To complete the input, just enter the "#" symbol - the program will exit the loop and use the Close () method to close the port.

#include #include #include"SerialGate.h" int main () ( int port = 1; int bool res = sg.Open (port, rate); if(res == false return 0; } else(printf ("Open OK! .. \ n");) printf ("Press key to send it to COM port. \ n"); printf ("Press" # "to exit. \ n \ n"); while(true) { char c = getch (); printf ("% c", c); sg.Send (& c, sizeof(c)); if(c == "#") break; ) sg.Close (); getch (); return 0; }

Here is such a simple application for sending data. Now let's write a second program that will open the second COM port connected to the first cable and read data from there. Finished application project COMClient can be downloaded in the heading of the article.

So, again, create an instance of the SerialGate class and open the "COM2" port at a speed of 57600 bps. I would like to draw your attention to the fact that for pre / data transfer, both ports participating in the exchange of information must be open at the same speed. Further, in an "endless" loop with an interval of one second, we read data from the COM port using the Recv () method. It needs to pass two parameters: the address to the buffer into which the read data will be placed and the size of this buffer in order to avoid going out of the array. The function returns the number of bytes read. Then the read data is printed byte-by-byte, and if the "#" character is found, then COMServer has finished working and this program needs to do the same.

#include #include #include"SerialGate.h" int main () ( int port = 2; int rate = 57600; SerialGate sg; bool res = sg.Open (port, rate); if(res == false) (printf ("Open Error .. \ n"); getch (); return 0; } else(printf ("Open OK! .. \ n");) printf ("Get data from COM port every 1 sec. \ n \ n"); char buf; int dwBytesRead = 0; bool terminate = false; while(! terminate) (Sleep (1000); dwBytesRead = sg.Recv (buf, sizeof(buf)); for(int i = 0; i if (buf [i] == "#") (terminate = true; break; ))) sg.Close (); return 0; }

Now is the time to test this entire system. Check if you have connected the ports with a cable. We compile and run both applications. In the COMServer program, we type some text from the keyboard - with some delay it should appear in the COMClient program window.


In the next articles, we will write a Windows application for receiving / transmitting data and consider other features of the library.

© Dmitry Ivanov
May 2007


Top