[RESOLVED] MSCOMM : Save received data as a Bitmap file
I coded an app which connects to a device through serial port. The device sends some values (numbers) followed by a bitmap image formed of 11518 characters/bytes starting with 'BM' header of course.
The problem is that the bitmap file I create from these 11518 characters (stored in rs232Data variable of string type) is corrupted!!
I am using the following code:
Code:
Open "D:\Test.bmp" For Binary As #1
Put #1, , rs232Data
Close #1
P.S : I have a similar app connected to a different device which I use the same code above but after decoding the Base64 data received and it works just fine, the current device does send normal ASCII (0-255) characters.
I do appreciate if someone can help.
Last edited by labmany; Oct 20th, 2017 at 06:45 AM.
Thanks dilettante, but I am not sure how to implement a byte array which stores the whole data stream till it hits a ETX character!
I mean the whole data will not be delivered at once so the OnComm event will be fired a few times and every time the byte array will be overwritten or am I missing something?!!
Code:
Dim rs232Data() As Byte
Private Sub MSComm1_OnComm()
rs232Data = MSComm1.Input
If rs232Data(UBound(rs232Data)) = Chr$(3) Then Call Finished
End Sub
Another option is to simply open a file and write the byte(s) returned by the input directly to the file. When ETX is received, close the file. Air code follows...
Code:
Dim hFile As Integer, rs232Data() As Byte
hFile = FreeFile()
Open "C:\...\rs232_uniqueKey.bmp" For Binary As #hFile
Private Sub MSComm1_OnComm()
rs232Data = MSComm1.Input
If hFile = 0 Then Exit Sub
If rs232Data(UBound(rs232Data)) = Chr$(3) Then
If UBound(rs232Data) > LBound(rs232Data) Then ' test against a 1 byte array
ReDim Preserve rs232Data(LBound(rs232Data) To UBound(rs232Data)-1)
Put #hFile, , rs232Data()
End If
Close #hFile
hFile = 0 ' can be checked in form unload or elsewhere to see if it's still open or closed
Call Finished
Else
Put #hFile, , rs232Data()
End If
End Sub
Edited: If aborting while receiving data, close the handle outside of the above routine and delete the file.
Last edited by LaVolpe; Oct 21st, 2017 at 09:07 AM.
Insomnia is just a byproduct of, "It can't be done"
You can't delimit the stream using &H03 ETX because that's a valid byte value in any binary data.
Excellent point. The value 0x03 can be part of the bitmap data/pixel info. Though one can still get through this. When enough bytes received 54+ (that should be the file header + minimal bitmap info header), then one can calculate how many bytes are required for the entire bitmap. Once that has been reached, close out the file and/or truncate the file as needed.
Insomnia is just a byproduct of, "It can't be done"
Or just use the file as the assembly technique and periodically check it (from within that same routine). Keeping track of number of bytes received would be a start. Once byte count reached 54+, needed byte count (for complete file) can be calculated and known.
Calculating where in the file the bitmap data actually ends isn't that difficult. Once the complete data is there, can simply strip out the needed data to a another, true, bitmap file. Of course, without some sort of packet telling how many bytes are expected, calculating needed bytes relies on the bitmap file/info headers being properly filled in. The size of the entire bitmap can be calculated from both the file header (1st 14 bytes where there's an actual field for that value) and the bitmap info header (next 40+ bytes) that will require in-depth knowledge of the various bitmap formats/header versions
Edited: pseudo-code
Code:
get bytes from comm
write bytes to data file
increment byte count: count = existing count + new bytes just received
If byte count = > 54 then calculate total bytes needed for bitmap
If byte count = > total needed byte count, extract data from data file to a new bitmap file
rinse & repeat
Last edited by LaVolpe; Oct 21st, 2017 at 01:38 PM.
Insomnia is just a byproduct of, "It can't be done"
Using files for this feels like linking several 1940s washing machines together with belts. If you can't use one of the many "binary string builder" classes that have been published here there is always the handy ADO Stream object.
Using files for this feels like linking several 1940s washing machines together with belts.
At least you can fix those
Point taken and I'd second the notion of an ADO stream. Regardless of the collection method, I think the logic I provided, in post #9 not post #3, should work unless the OP wants to hope that whenever the last byte in the current burst is 0x03, it also happens to be the end of the stream.
Last edited by LaVolpe; Oct 21st, 2017 at 02:28 PM.
Reason: clarification of which logic
Insomnia is just a byproduct of, "It can't be done"
Well it seems very flawed to have that ETX at the end, but if it is going to be sent after the image data he should be sure to consume and discard the extra byte if more data is to follow.
Where:
{STX} = Chr$(2)
{ETX} = Chr$(3)
n = numbers from 0 to 9
DDMMYYYY = Date
IM1BMP11518 = IM1 first image of BMP type with length of 11518 bytes.
IM2BMP11518 = IM2 first image of BMP type with length of 11518 bytes.
IM3BMP11518 = IM3 first image of BMP type with length of 11518 bytes.
xxx...xxx = 11518 bytes adding "BM" to the start of them
The whole data stream is fixed length every time the device send it, I parse the numeric values, get the start of the first BMP file using Instr(rs232Data,"IM1BMP") + 11 and the length of it is just 11518 bytes long and save it as binary file, get the second one, the third one and I am done.
All numeric values are perfect, Bitmap files are corrupted!!!
Last edited by labmany; Oct 21st, 2017 at 09:08 PM.
The most straightforward way to handle this has already been described.
Accept the data as binary, extract any ANSI substrings you need like your "numbers" and convert them. Write the segments containing a bitmap to disk.
Text mode converts each Byte or Bytes received to Unicode String values. It was only meant to be used when you receive strictly ASCII data.
As far as I can see your data looks fine. Clearly as it was poured "from glass to glass" multiple times converting to Unicode and back to ANSI when writing to disk... you got lucky. Mainly because there were no locale changes between conversions. However even for the same locale some bit patterns might not survive without distortion.
But it looks like it should be ok if you separate out the bitmap segments and write those to disk separately. I wouldn't do it, but it may work most of the time for you.
I does appear you are interpreting the stream correctly. If extracting from the cached data the byte where "BM" (not BMP) is found and extracting the 11518 bytes, it is a complete, valid bitmap. Looks like a graph/chart. I did look at the 1st bitmap a bit closely to verify the 11518 is correct and it was. I don't know if that value came from the bitmap file header (included in the stream) or not. If it did, hopefully all of them are correct if you are going to rely on it. However, if all your bitmaps are 16 color, 150x150 then the size of each 'file' should be the same.
Insomnia is just a byproduct of, "It can't be done"
Good, below is a routine, which is able to parse-out the contained Bitmap-Parts from your incoming ByteArray-Data
(for the demo, your posted file-content was used, after placing it in "c:\temp\test.txt" - the resulting Bitmap-Files are also placed there):
Code:
Option Explicit
Private Sub Form_Load()
Dim B() As Byte
ReadWriteBytes "C:\temp\test.txt", B, True 'read the Input-ByteArray from disk
ParseBytes B 'make an attempt to parse what's in there, BitMap-wise
End Sub
Private Sub ReadWriteBytes(FileName, B() As Byte, ByVal ReadThem As Boolean)
Dim FNr As Long
FNr = FreeFile: Open FileName For Binary As FNr
If ReadThem Then ReDim B(0 To LOF(FNr) - 1): Get FNr, , B Else Put FNr, , B
Close FNr
End Sub
Private Sub ParseBytes(B() As Byte)
Dim i As Long, j As Long, L1 As Long, L2 As Long, BMP() As Byte
For i = 0 To UBound(B) - 20
If B(i) = 66 And B(i + 1) = 77 And B(i + 2) = 80 Then
L1 = 0: L2 = 0
For j = i + 3 To i + 13
If B(j) = 66 And B(j + 1) = 77 Then
L1 = B(j + 2) + 256& * B(j + 3) + 65536 * B(j + 4)
Exit For
End If
Next
If L1 > 0 Then
For j = i + 3 To j - 1: L2 = L2 * 10 + B(j) - 48: Next
If L1 = L2 And j + L1 <= UBound(B) Then
i = j: ReDim BMP(0 To L1 - 1)
For j = 0 To UBound(BMP): BMP(j) = B(i + j): Next
'the BMP-ByteArray is now filled... (here we simply write it to disk)
ReadWriteBytes "C:\temp\BitmapAtOffs" & i & ".bmp", BMP, False
End If
End If
End If
Next
End Sub
The 3 Bitmaps are also written to "c:\temp\..." - here is the last of the 3 Bitmaps which were found:
Good, below is a routine, which is able to parse-out the contained Bitmap-Parts from your incoming ByteArray-Data
(for the demo, your posted file-content was used, after placing it in "c:\temp\test.txt" - the resulting Bitmap-Files are also placed there):
Code:
Option Explicit
Private Sub Form_Load()
Dim B() As Byte
ReadWriteBytes "C:\temp\test.txt", B, True 'read the Input-ByteArray from disk
ParseBytes B 'make an attempt to parse what's in there, BitMap-wise
End Sub
Private Sub ReadWriteBytes(FileName, B() As Byte, ByVal ReadThem As Boolean)
Dim FNr As Long
FNr = FreeFile: Open FileName For Binary As FNr
If ReadThem Then ReDim B(0 To LOF(FNr) - 1): Get FNr, , B Else Put FNr, , B
Close FNr
End Sub
Private Sub ParseBytes(B() As Byte)
Dim i As Long, j As Long, L1 As Long, L2 As Long, BMP() As Byte
For i = 0 To UBound(B) - 20
If B(i) = 66 And B(i + 1) = 77 And B(i + 2) = 80 Then
L1 = 0: L2 = 0
For j = i + 3 To i + 13
If B(j) = 66 And B(j + 1) = 77 Then
L1 = B(j + 2) + 256& * B(j + 3) + 65536 * B(j + 4)
Exit For
End If
Next
If L1 > 0 Then
For j = i + 3 To j - 1: L2 = L2 * 10 + B(j) - 48: Next
If L1 = L2 And j + L1 <= UBound(B) Then
i = j: ReDim BMP(0 To L1 - 1)
For j = 0 To UBound(BMP): BMP(j) = B(i + j): Next
'the BMP-ByteArray is now filled... (here we simply write it to disk)
ReadWriteBytes "C:\temp\BitmapAtOffs" & i & ".bmp", BMP, False
End If
End If
End If
Next
End Sub
The 3 Bitmaps are also written to "c:\temp\..." - here is the last of the 3 Bitmaps which were found:
HTH
Olaf
Thanks Olaf, you made my day
By the way, I can not wait to see your vbRichClient compiler