Need Help with MSComm on PC to Read Serial Binary Data Stream - Page 1
Forum section: electronics / general

author: SuzyC
author_rank: Frequent Contributor
timestamp: November 06, 2016, 04:41:08 am
content: I hope some of you code gurus are in a nostalgic legacy mood and give me a helping hand.I want to input data into a PC using COM1: serial port using MSComm within VB6 SP6. I have no trouble transmitting a binary stream from a MCU to the PC but I don't know how to read the MSComm input buffer when the binary data has being received.I want to transfer data as fast as possible, and COM1: port is opened and set to work with 115200, n, 8,1 MSComm.SettingsIf wanted to deal with the byte overhead, I could send everything using literal hex characters, but that slows things down!. I would have to make time-consuming int to ascii conversions on the MCU end and ASCII to integer conversions on the PC side.Of course, then the byte buffer would be all ASCII 0-F hex literals and I wouldn't have a problem even if the stream was one big slow to process string terminated by a single 0x00 null character.I am sending a fixed-length stream of integers to the PC with a 5-byte start-of-stream constant preamble followed by each bytes id number and then the hi and lo bytes of the integers one after another.I have configured the MSComm settings so that MSComm is expecting to receive binary rather than ASCII data.Example: 00 00 00 00 0x1B preamble (to detect start of stream)then I would send the first id and word, for example id=1 then 16-bit unsigned integer (=1.)..id=2..16bit unsigned int ..etc until 96 integers have been sent and the serial frame ends until the next transmission.Integers are sent in big endian order. This means the buffer should contain 5 +(96*3) bytes = 293 bytes total/frame.Now I can easily set the input buffer size and read input buffer number of bytes, but there is more devil to detail to get my bytes back.So if I were to send (preamble) id=1 integer =1..id=2 integer=100...So the stream would look like (in Hex) 00 00 00 00 1B 01 00 01 02 00 64 ...etcMy question is how to read the MSComm buffer to retrieve my binary data?Thanks for your help!

author: JPortici
author_rank: Super Contributor
timestamp: November 06, 2016, 09:15:26 am
content: if i understood what you want to do..why do you think you need to convert binary to ascii on the mcu side?reading mscomm gives you ascii one byte at a time, ok. so just use CInt(bufferblah) and you have your raw 8bit back. then you concatenate them into other int or long or whatever.. i do it all the timebuffer(0) = CInt(rxbuffer)...buffer(1) = CInt(rxbuffer)data = buffer(0) * 256 + buffer(1) <--- thi actually depends on the endianness of your data

author: SuzyC
author_rank: Frequent Contributor
timestamp: November 07, 2016, 01:51:33 am
content: In my serially endiendless fight for answers such as to seek Cintches in my search of sequestered information, Jportici, your comm-ents stalls short of sure help.

author: cv007
author_rank: Frequent Contributor
timestamp: November 07, 2016, 06:19:19 am
content: https://msdn.microsoft.com/en-us/library/aa259414(v=vs.60).aspx

author: SuzyC
author_rank: Frequent Contributor
timestamp: November 07, 2016, 12:29:23 pm
content: 'Thanks a Gigabit, cv007, but I have already set:B$ = Trim(BaudRate)MSComm1.Settings = B$ & ",n,8,1"MSComm1.InputMode = comInputModeBinaryMSComm1.InBufferSize = 2048MSComm1.InBufferCount = 0 'Clears bufferMSComm1.InputLen = 0MSComm1.PortOpen = True'and I (attempt to) get my data with:'.............TxtNumBytesInBuf = MSComm1.InBufferCountIf (MSComm1.InBufferCount = FrameSize) ThenB$ = ""B$ = B$ & MSComm1.InputUpdates = Updates + 1Txtupd = UpdatesMSComm1.InBufferCount = 0 'Clears BufferEnd IfGoTo ExitComit'........B$ contains garbage, and surprisingly, always has only half the number of bytes of FrameSize.For instance, if I send 8 bytes (values shown indecimal) 0 0 0 0 27 1 0 1Then I parse and look at each of the total of four bytes:for j=1 to 4A$ = Mid$(B$,j,1): bite=Asc(B$)Print bite;" ";next jResult: Always yields the same bite values: 0 0 103 63No Workee.

author: Keicar
author_rank: Regular Contributor
timestamp: November 07, 2016, 01:03:13 pm
content: Have you tried using an array of bytes instead of a string variable to store the contents of MSComm1.Input? Not sure it'll help, but it is the actual data type that being returned when InputMode is set to comInputModeBinary.

author: PA0PBZ
author_rank: Super Contributor
timestamp: November 07, 2016, 01:16:55 pm
content: Quote from: SuzyC on November 07, 2016, 12:29:23 pmReplace that byB$ = MSComm1.Input(just because...)QuoteWrong baudrate?

author: Keicar
author_rank: Regular Contributor
timestamp: November 07, 2016, 01:25:33 pm
content: The native string type in VB6 is the OLE Automation BSTR, which is a null-terminated array of 16-bit Unicode characters, preceded by a 32-bit length marker (which resides in the 4 bytespriorto the address pointed to by the BSTR).The fact that VB6 strings are Unicode may well be related to your getting half the number of characters you're expecting - it would seem that assigning an array of bytes to a string doesn't work the way you might expect. Since each character is 16 bits, it assignstwobytes to each one, I suspect.

author: Buriedcode
author_rank: Super Contributor
timestamp: November 07, 2016, 04:27:59 pm
content: Second on the byte encoding. I would keep your incoming bytes as just.. bytes and not ASCII. The old MScomm as well as the newer (terribly implemented) .NET serial controls are all geared towards true terminal comms - ASCII, linefeed, carriage return etc.. rather than raw bytes, but of course can be configured for such.Whilst its nice to be able to use string functions, I have had to use very specific coding to get actual single byte ASCII values, rather than the standard 16-bit encoding. I believe it was.. crudely converted from C#...Code:s = System.Text.Encoding.GetEncoding(28591).GetChars(dataBytes.ToArray, 0, EOLlocation) 'gets all the bytes up until linefeed characterNote the dodgy number, which I got from an hour of googling.In the embedded world ASCII is simple, but there are many forms of ASCII encoding on PC's.As for dealing with frames and fixed-size packet data, I never relied on built in events to fire when a terminal character was found, or when a certain number of bytes has been received. This is mostly related to the newer .NET controls (3.5+) but Ivaguelyremember the same deal with MScomm (when I was 12 :/). It sometimes didn't fire when a certain number of bytes was received, or it did, but only returned half of what it had. Also, both versions rarely reported the correct number of bytes in its buffer: you get the number of bytes it claims it has recieved, create an array of that size and copy over, only for an exception to be thrown because in the time it took to move the data through many levels of buffers - you've received a few more bytes, and the array you created is too small.Crude solution which might not be so great on VB6 was to set up a background worker thread on a timer. This simply, periodically grabbedallthe data from the receive buffer (by seeing how many it claims were received, adding to a large array, resizing the array based on how many were actually received, then copying), and added that chunk to a byte list, which then triggered an event in the main thread to grab that, add to to the end of the main buffer, check for start/termination values and spit out packets when it found them. Effectively doing it manually because it was the only way I could get all the bytes in at > 19.2k. (seems to work at >1Mbps using USB-serial adapters). It shouldn't have to be this way, but also was less prone to hanging.That said people still use MScomm today because for legacy apps its more reliable than the later .NET and...ahem.. Java... for serial comms.

author: hendorog
author_rank: Super Contributor
timestamp: November 07, 2016, 07:11:49 pm
content: Quote from: Buriedcode on November 07, 2016, 04:27:59 pmJava serial comms worked fine when I used it many years ago - we did several multidrop RS-485 installations with it. We moved to java as it was easier to avoid memory leaks than C/C++ or Delphi, and easier to maintain than VB (OCX hell). The other bonus was it would run on linux which avoided the pain of having a headless windows box sitting in an industrial cabinet.I've been using Java serial comms over USB recently, and it has been working perfectly.

author: Buriedcode
author_rank: Super Contributor
timestamp: November 08, 2016, 12:54:08 am
content: I guess I am a bit out of touch then, haven't used java that much in the past er.. 8 years?Well, not directly..

author: SuzyC
author_rank: Frequent Contributor
timestamp: November 08, 2016, 05:55:39 am
content: Thanks Keicar,that means MSComm binary input mode is useless or just very clumsy for receiving binary bytes with VB6 SP6.Thanks PAOPBZ. but your change didn't change the result. The baudrate is what it needs to be: 115200,n,8,1Thanks Buriedcode. I am using a Timer Control that is set to fire every 3-mSec to invoke reading the input buffer. It must be working fine because my received bytes are wrong but entirely consistently wrong each time in values.Thanks henderog, but at least I know using the VB6 MSComm .OCX code will not have to face compatibility issues with other Windows versions that might belch with any .OCX. For the automated machine control job program I am coding(only to run on Windows XP SP6 ), my choice of VB6 programming is ideal, it is just so easy and so readable, yet powerful and versatile enough.VB6 is so simple to code with compared to any newer .net, C++. C#. Java on Unix etc.

author: hendorog
author_rank: Super Contributor
timestamp: November 08, 2016, 06:11:21 am
content: Quote from: SuzyC on November 08, 2016, 05:55:39 amQuite, and I was not suggesting you need to change, I was replying to BuriedCode. I actually did a bit of VB back in the day - starting with VB 1.0 and some later versions which I can't remember.Anyway, if I was going to suggest an alternative (for simpler VB apps) it would probably be Processing.org. Same IDE as Arduino and really easy to get into.

author: Keicar
author_rank: Regular Contributor
timestamp: November 08, 2016, 10:49:06 am
content: I guess if you really need the received data in a string you could always assemble it yourself, just iterate through the array of bytes received from MSComm.Input and concatenate each one to your string with CHR$() - not exactly elegant, but it should at least work.

author: SuzyC
author_rank: Frequent Contributor
timestamp: November 08, 2016, 01:02:44 pm
content: Thanks keicar, but your solution falls flat with the third double-byte = 103..The recovered code should be the same as sent(in decimal) 0 0 0 0 27 1 0 1Your method changes the received 4 16-bit ascil words from 0 0 103 63 (decimal) into 0 0 0 0 0 163 0 63This is giving up figuring out the problem to hacking it and not getting the proper results.It can't be that complicated!Thanks hendorog, but switching to some type of Arduino codiing is just throwing away the baby with the bath water, again skirting around the problem which obviously must have a simple solution, yet to be revealed here.It is sad to note that Microsoft's documentation did not present just a few VB lines of code clearly showing how to use MSComm with binary data.https://msdn.microsoft.com/en-us/library/aa259414(v=vs.60).aspxClearly says to set data input mode to binary to receive binary data, but some other code details are needed to rescue my bytes from the receive buffer.It can't be that MSComm is an incompetent buggy .OCX or that it hasn't been successfully implemented by thousands of coders working with binary data who happen to somehow know the proper coding to get it to work perfectly.

author: SuzyC
author_rank: Frequent Contributor
timestamp: November 08, 2016, 01:30:37 pm
content: Very interesting...If Iswitch input mode from binary to ASCIIIf (MSComm1.PortOpen = True) Then MSComm1.PortOpen = False 'Clears any ErrorsMSComm1.CommPort = 1B$ = Trim(BaudRate)MSComm1.Settings = B$ & ",n,8,1"MSComm1.InputMode= 0 'changed to ASCII from comInputModeBinaryMSComm1.NullDiscard = FalseMSComm1.InBufferSize = 2048MSComm1.InBufferCount = 0 'Clears bufferMSComm1.InputLen = 0 'Allows all of the buffer to be read into a single stringMSComm1.PortOpen = TrueThen all 293 binary bytes are consistently received perfectly and in their proper transmitted order into B$ with:TxtNumBytesInBuf = MSComm1.InBufferCountIf (MSComm1.InBufferCount = FrameSize) Then 'Frame Size is 293 for preamble of 5-bytes followed by 96 integers in the form id..integer..id.. integer ... all in binary.B$ = ""B$ = B$ & MSComm1.InputMSComm1.InBufferCount = 0 'Clears Buffer, ready to receive next Xmitted frame.End IfProblem solved...buy why could Microsoft be so misleading!