Jump to content
madyn

Suggestions needed for serial (RS232 style) packet interface

Recommended Posts

Background first, if you please:

RadStudio 12.2, in C++ on Windows 11.
Monitors status information from up to 4 microprocessor nodes using TTL to USB converters.  Each node is a separate board with its own interface.

Ideally, I use a serial port package in C++ which (non-blocking) reads a package and updates a data panel on the display.  Decoding the packet is no problem, getting it, is.
Communication is in ASCII encoded packets to avoid premature terminating zeros, so string reads will work.

Tried the APRO interface, but documentation seems to be lacking, with lots of features in the packages I don't need and no basic startup information.

Tried the BOOST rabbit hole, which I somewhat got to work (gotta love the documentation (if any)).  and then lost the solution.  It seems to be blocking only, which I could tolerate if I could create a task.

I could do this with a microprocessor, it would simply mirror the drivers in the hardware, using FreeRTOS tasks to read each port (one task = 1 port), and tasks can be blocking without a problem.

 

Anybody know of a well documented serial package that can do non-blocking reads, C++ compatible?  Then I need to do multiple threads, haven't looked at that yet in C++.

 

I don't really have a problem with C++, but the add-ons are a problem.  Got the UI (more or less customizable) up and running, custom buttons (made from panels) can be enabled/disabled with different text for pressed, different colors when active.

so the C++ is not a problem.

Thanks in advance

 

Harvey

 

 

Share this post


Link to post

There are lot of packages for Delphi and C++, look at GETIT from IDE and search for:

 

1) asyncpro

or

2) serial

 

I have been using this component for a long time too: https://sourceforge.net/projects/comport/files/comport/4.11/comport411f.zip/download

 

To read the Help you must use an old WinHelp executable. You can discharge it from Microsoft (I had ones for Windows 7 and is working in Win 11).

 

Bye

Edited by DelphiUdIT

Share this post


Link to post

SERIAL give me a program that is both commercial and one that Norton (bless it's little heart) rejects because it tried to modify a protected file.

I'm looking at asyncpro, because I know now how to find open ports (which async pro doesn't do, and I needed).  If it knows how to actually read data I may do that.

My basic scheme is to scan ports, then select which port is which (having retrieved the open ports); then allow selecting a port connecting to which board (you'd have to see the overall setup for it to make real sense); then assign that to a node panel which reports the results from a board assigned to a particular com port and thus a particular board.

Harvey

 

Share this post


Link to post
2 hours ago, Jirka52 said:

You can create trhread with TComport for communication with your device.

Component is here, install it and use it in your code.

Comport is Delphi component, but it works also in C++ Builder, I have been using it in C++ Builder more than 14 years.

https://sourceforge.net/projects/comport/

 

I suggested the same in my previous post :classic_biggrin:

  • Like 1

Share this post


Link to post

I've been using Async Pro for over 20 years, well documented, the original version was sold in a box with a book.  Now I install it from GetIt.

 

But I updated the TApdCustomComPort.InitializePort method so it can open COM ports by name instead of simple numbers, since many physical and virtual COM ports have more complex name than COM1 and COM2, like CNCA0.

 

I have a free component that locates and lists COM ports, https://www.magsys.co.uk/delphi/maghardware.asp

 

Angus

 

  • Like 2

Share this post


Link to post

OK, spent some time getting things to (somewhat) work.

Comport scanning works, and returns an ID of what the friendly name of the comport happens to be.

I went with APRO since it was available and installed.  Not sure exactly how to install a delphi package in C++ builder, but for now, that doesn't seem to be needed.

Finally got the idea of how to specify packet start and stop, (documentation is for Delphi), and I was overcomplicating things.

 

This is the data structure, and it is being sent (terminal program says so).

 

struct SYSTEM_SUPERUSER_packet_data
{
	char								SOH;			// start of header = begin packet
	char								id[8];			// 32 bits of processor ID code hex
	char								STX;			// STX = beginning of block
	union SUPERUSER_BLOCK				block;			// block data in message (hex(
	char								ETX;			// ETX = end of block
	char								checksum[4];	// checksum, should add to zero;
	char								EOT;			// end of transmission = end packet
	char								CR;
	char								LF;
};

//	super_packet.p.EOT = 0x04;
//	super_packet.p.ETX = 0x03;
//	super_packet.p.SOH = 0x01;
//	super_packet.p.STX = 0x02;
//	super_packet.p.CR = 0x0D;
//	super_packet.p.LF = 0x0A;




//  this is sent to the computer as a data packet
union SYSTEM_SUPERUSER_packet_type
{
	struct SYSTEM_SUPERUSER_packet_data		p;
	uint8_t									b[sizeof(struct SYSTEM_SUPERUSER_packet_data)];
};

 

Problem now is that the ApdDataPacket component doesn't work all the time.  the ApdDataPacket1Packet code does not always get executed, things get ignored.

Triggers are the SOH and the EOT.

 

Any suggestions?

 

Harvey

 

 

Share this post


Link to post

I just use the OnTriggerAvail event to receive all data available, add it to a buffer, locate my packet, remove from buffer, repeat.  More code that, but more reliable, particularly if the packet can contain random binary data, like your triggers. 

 

Angus

 

Share this post


Link to post

OK, got this working.  Thanks for the help here,

I added a circular buffer so I always have room.

Things I needed to know (and are apparently true)

  1. On trigger apparently fires once per character
  2. Packet decoding is not all that reliable for me
  3. the circular buffer uses std::optional, which is a whole another world to deal with
    1. until you understand it
  4. APro's documentation is lacking,
    1. it would be nice to have had C++ documentation
    2. it would be nice to have more details on how things work

The code I ended up with follows, in case anyone has the same need:



// client node panel[x] owns circular buffer
void __fastcall TForm1::ApdComPort1TriggerAvail(TObject *CP, WORD Count)
{
	uint8_t                     buffer[256] = {0};
	int                         i;

	std::string                      data;

	ApdComPort1->GetBlock(buffer,Count);

	i = 0;
	while ((buffer[i] != 0) && (i < 250))
	{
		// put chars in circular buffer
		ClientNodePanel[0]->NODE_buffer.put(buffer[i]);
		// packet ends with linefeed
		if (buffer[i] == 0x0A)
		{
			// end of packet, copy data into data structure
			int j = 0;
			while (!ClientNodePanel[0]->NODE_buffer.empty())
			{
				// data as stored in circular buffer is in std::optional
				std::optional<uint8_t> gchar = ClientNodePanel[0]->NODE_buffer.get();
				// actual value read, append to data string for memo display
				data.append(1,gchar.value());
				// copy to binary overlay of data structure
				superuser_packet[0].b[j++] = gchar.value();
			}
			// display
			Memo1->Lines->Add(data.c_str());
			// update display with decoded data
			ClientNodePanel[0]->update(superuser_packet[0]);
		}
		i ++;
	}
}

 

Comments welcome:

 

Harvey

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×