judico
asked on
Questions to drichards regarding the managed code for the multimeter
drichards,
I’m trying to transform the managed code you wrote for the 22-812 multimeter into one that can read data from a PIC device. In VB.NET I’m using the following code to read from that device:
Public Class Form1
Inherits System.Windows.Forms.Form
Dim comms_ok As Boolean = False
<Windows Form Designer generated code>
Public Sub rS232Comms1_DataRxEvent(By Val sender As System.Object, ByVal e As RS232.RxEventArgs) Handles RS232Comms1.DataRxEvent
Dim LengthOfString As Integer
Dim buffer() As Byte = e.data
Dim oEncoder As New System.Text.ASCIIEncoding
Dim oEnc As System.Text.Encoding = oEncoder.GetEncoding(1252)
Dim byteValueString As String
Dim byteValueInteger0 As Double ' This is the value of the first Channel (Channel 0)
Dim byteValueInteger1 As Double ' This is the value of the second Channel (Channel 1)
Dim byteValueInteger2 As Double ' This is the value of the third Channel (Channel 2)
Dim byteValueInteger3 As Double ' This is the value of the fourth Channel (Channel 4)
Dim millivolts0 As Double
Dim millivolts1 As Double
Dim millivolts2 As Double
Dim millivolts3 As Double
Dim FirstNumber As Integer
Dim SecondNumber As Integer
Dim ThirdNumber As Integer
Dim FourthNumber As Integer
TextBox1.Clear()
‘ This gives the length of the string which is obtained from the port
LengthOfString = CInt(e.data.Length.ToStrin g)
‘ This converts the byte sent by the device into an ASCII number – see the Dim’s above – they are instrumental in receiving that byte
byteValueString = oEnc.GetString(buffer)
‘ Here the separate four sets of data are extracted from the common string byteValueString
Try
FirstNumber = byteValueString.Substring( 1, 6)
SecondNumber = byteValueString.Substring( 9, 5)
ThirdNumber = byteValueString.Substring( 15, 6)
FourthNumber = byteValueString.Substring( 22, 6)
Catch ex As Exception
Finally
End Try
‘ This is where the above-obtained parts of the string byteValueString are converted into Integer values
Try
‘ This gives the voltage
byteValueInteger0 = CInt(byteValueString.Subst ring(1, 6))
millivolts0 = (CInt(byteValueInteger0) / 1023) * 5
millivolts0 = (Int(millivolts0 * 1000)) / 1000
TextBox1.AppendText(" " & CStr(millivolts0) & " V" & " " & Chr(13) & Chr(10))
byteValueInteger1 = CInt(SecondNumber)
millivolts1 = (CInt(byteValueInteger1) / 1023) * 5
millivolts1 = (Int(millivolts1 * 1000)) / 1000
TextBox1.AppendText(" " & CStr(millivolts1) & " V" & " " & Chr(13) & Chr(10))
byteValueInteger2 = CInt(ThirdNumber) '
millivolts2 = (CInt(byteValueInteger2) / 1023) * 5
millivolts2 = (Int(millivolts2 * 1000)) / 1000
TextBox1.AppendText(" " & CStr(millivolts2) & " V" & " " & Chr(13) & Chr(10))
byteValueInteger3 = CInt(FourthNumber)
millivolts3 = (CInt(byteValueInteger3) / 1023) * 5
millivolts3 = (Int(millivolts3 * 1000)) / 1000
TextBox1.AppendText(" " & CStr(millivolts3) & " V")
Catch ex As Exception
Finally
End Try
End Sub
' THE END
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
'If comms_ok Then
RS232Comms1.CloseComms()
TextBox1.Clear()
TextBox1.AppendText("Cya ...")
'End If
Me.Dispose()
End Sub
End Class
To use the above code one has to add in the toolbox a special item – rs232comms.dll. Also I should mention that my PIC device has the following characteristics:
Baud rate = 2400
Byte size = 7
Parity = No parity
Stop bits = 2
The heart of the above code (aside from installing the rs232comms.dll) are the lines
Dim buffer() As Byte = e.data
Dim oEncoder As New System.Text.ASCIIEncoding
Dim oEnc As System.Text.Encoding = oEncoder.GetEncoding(1252)
which allow the incoming data ‘e.data’ to be stored in the variable ‘buffer’ which later can be converted into a string value byteValueString:
byteValueString = oEnc.GetString(buffer)
This byteValueString is exactly what I want to obtain with the managed code in Visual C++ .NET and so far I haven’t been able to.
In the code you wrote, the corresponding numerical value (a real number) to the string byteValueString from the above code is dMeterReading. The whole exercise is to obtain the value of that dMeterReading.
Now, to accomplish the above goal, I can see how you have put all the primitives of the necessary functions in the __gc class MultiMeter in the file MultiMeter.h. The functions themselves you have placed in the accompanying MultiMeter.cpp file and, of course, there is a file parameters.h containing a bunch of definitions.
This is the place where it gets confusing to me. For instance, I don’t understand what the meaning of the definitions:
#define SERIAL_READ_PKT_SIZE 9
#define SERIAL_READ_BUF_SIZE 4 * SERIAL_READ_PKT_SIZE
is and should they be changed for my PIC device?
Further, the most important function, namely MultiMeter::ReadData(), which returns the needed dMeterReading when called, assigns a value to that dMeterReading coming from a function which is specific for the 22-812 multimeter (because it calls MultiMeter::Translate22812 (BYTE * bBuf)) and does not seem to have anything to do with a PIC device. The PIC device sends data in one compact byte which only needs to be converted into string and doesn’t need the complicated procedure of translation through the function MultiMeter::Translate22812 (BYTE * bBuf).
How is this complication caused by MultiMeter::Translate22812 (BYTE * bBuf) to be avoided so that the data from the device be stored in one byte and how is this one byte to be converted into a string? This seems to be the main problem I’m having now.
I’m trying to transform the managed code you wrote for the 22-812 multimeter into one that can read data from a PIC device. In VB.NET I’m using the following code to read from that device:
Public Class Form1
Inherits System.Windows.Forms.Form
Dim comms_ok As Boolean = False
<Windows Form Designer generated code>
Public Sub rS232Comms1_DataRxEvent(By
Dim LengthOfString As Integer
Dim buffer() As Byte = e.data
Dim oEncoder As New System.Text.ASCIIEncoding
Dim oEnc As System.Text.Encoding = oEncoder.GetEncoding(1252)
Dim byteValueString As String
Dim byteValueInteger0 As Double ' This is the value of the first Channel (Channel 0)
Dim byteValueInteger1 As Double ' This is the value of the second Channel (Channel 1)
Dim byteValueInteger2 As Double ' This is the value of the third Channel (Channel 2)
Dim byteValueInteger3 As Double ' This is the value of the fourth Channel (Channel 4)
Dim millivolts0 As Double
Dim millivolts1 As Double
Dim millivolts2 As Double
Dim millivolts3 As Double
Dim FirstNumber As Integer
Dim SecondNumber As Integer
Dim ThirdNumber As Integer
Dim FourthNumber As Integer
TextBox1.Clear()
‘ This gives the length of the string which is obtained from the port
LengthOfString = CInt(e.data.Length.ToStrin
‘ This converts the byte sent by the device into an ASCII number – see the Dim’s above – they are instrumental in receiving that byte
byteValueString = oEnc.GetString(buffer)
‘ Here the separate four sets of data are extracted from the common string byteValueString
Try
FirstNumber = byteValueString.Substring(
SecondNumber = byteValueString.Substring(
ThirdNumber = byteValueString.Substring(
FourthNumber = byteValueString.Substring(
Catch ex As Exception
Finally
End Try
‘ This is where the above-obtained parts of the string byteValueString are converted into Integer values
Try
‘ This gives the voltage
byteValueInteger0 = CInt(byteValueString.Subst
millivolts0 = (CInt(byteValueInteger0) / 1023) * 5
millivolts0 = (Int(millivolts0 * 1000)) / 1000
TextBox1.AppendText(" " & CStr(millivolts0) & " V" & " " & Chr(13) & Chr(10))
byteValueInteger1 = CInt(SecondNumber)
millivolts1 = (CInt(byteValueInteger1) / 1023) * 5
millivolts1 = (Int(millivolts1 * 1000)) / 1000
TextBox1.AppendText(" " & CStr(millivolts1) & " V" & " " & Chr(13) & Chr(10))
byteValueInteger2 = CInt(ThirdNumber) '
millivolts2 = (CInt(byteValueInteger2) / 1023) * 5
millivolts2 = (Int(millivolts2 * 1000)) / 1000
TextBox1.AppendText(" " & CStr(millivolts2) & " V" & " " & Chr(13) & Chr(10))
byteValueInteger3 = CInt(FourthNumber)
millivolts3 = (CInt(byteValueInteger3) / 1023) * 5
millivolts3 = (Int(millivolts3 * 1000)) / 1000
TextBox1.AppendText(" " & CStr(millivolts3) & " V")
Catch ex As Exception
Finally
End Try
End Sub
' THE END
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
'If comms_ok Then
RS232Comms1.CloseComms()
TextBox1.Clear()
TextBox1.AppendText("Cya ...")
'End If
Me.Dispose()
End Sub
End Class
To use the above code one has to add in the toolbox a special item – rs232comms.dll. Also I should mention that my PIC device has the following characteristics:
Baud rate = 2400
Byte size = 7
Parity = No parity
Stop bits = 2
The heart of the above code (aside from installing the rs232comms.dll) are the lines
Dim buffer() As Byte = e.data
Dim oEncoder As New System.Text.ASCIIEncoding
Dim oEnc As System.Text.Encoding = oEncoder.GetEncoding(1252)
which allow the incoming data ‘e.data’ to be stored in the variable ‘buffer’ which later can be converted into a string value byteValueString:
byteValueString = oEnc.GetString(buffer)
This byteValueString is exactly what I want to obtain with the managed code in Visual C++ .NET and so far I haven’t been able to.
In the code you wrote, the corresponding numerical value (a real number) to the string byteValueString from the above code is dMeterReading. The whole exercise is to obtain the value of that dMeterReading.
Now, to accomplish the above goal, I can see how you have put all the primitives of the necessary functions in the __gc class MultiMeter in the file MultiMeter.h. The functions themselves you have placed in the accompanying MultiMeter.cpp file and, of course, there is a file parameters.h containing a bunch of definitions.
This is the place where it gets confusing to me. For instance, I don’t understand what the meaning of the definitions:
#define SERIAL_READ_PKT_SIZE 9
#define SERIAL_READ_BUF_SIZE 4 * SERIAL_READ_PKT_SIZE
is and should they be changed for my PIC device?
Further, the most important function, namely MultiMeter::ReadData(), which returns the needed dMeterReading when called, assigns a value to that dMeterReading coming from a function which is specific for the 22-812 multimeter (because it calls MultiMeter::Translate22812
How is this complication caused by MultiMeter::Translate22812
ASKER
In VB.NET, the data received from the device is contained in the byte variable 'e.data'. That 'e.data' is assigned to the variable 'buffer()' through
Dim buffer() As Byte = e.data
Then the byte 'buffer' is converted into the string 'byteValueString' through:
byteValueString = oEnc.GetString(buffer)
And now, this is what I get when the value of the string 'byteValueString' is displayed via TextBox2.Text = (byteValueString):
0 1023
1 1023
2 1023
3 1023
These 1023's are the maximum values read by the inputs. Above is the format of the data which I later use un the rest part of the program.
Could you please send me some code in VC++ which could do the same?
Dim buffer() As Byte = e.data
Then the byte 'buffer' is converted into the string 'byteValueString' through:
byteValueString = oEnc.GetString(buffer)
And now, this is what I get when the value of the string 'byteValueString' is displayed via TextBox2.Text = (byteValueString):
0 1023
1 1023
2 1023
3 1023
These 1023's are the maximum values read by the inputs. Above is the format of the data which I later use un the rest part of the program.
Could you please send me some code in VC++ which could do the same?
Not sure what you need here. Over in C++-land, you are doing the serial port read. The bytes read are really just some string representations of numbers? Your VB code does not match the format of the output you show:
FirstNumber = byteValueString.Substring( 1, 6)
SecondNumber = byteValueString.Substring( 9, 5)
ThirdNumber = byteValueString.Substring( 15, 6)
FourthNumber = byteValueString.Substring( 22, 6)
should not correctly parse:
0 1023
1 1023
2 1023
3 1023
What is the expected format of the data coming off the serial port?
FirstNumber = byteValueString.Substring(
SecondNumber = byteValueString.Substring(
ThirdNumber = byteValueString.Substring(
FourthNumber = byteValueString.Substring(
should not correctly parse:
0 1023
1 1023
2 1023
3 1023
What is the expected format of the data coming off the serial port?
ASKER
See, I'm not sure why this parsing worked. I arrived it through trial and error since I don't know exactly what the expected format coming off the serial port is.
ASKER
See, I'm not sure why this parsing worked. I arrived at it through trial and error since I don't know exactly what the expected format coming off the serial port is.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks a lot for the code. I had to make some minor modifications. One of them was that for some reason dwRead = ReadPort(buf, 32); had to be changed to:
dwRead = ReadPort(buf, 27);
I don’t know why this should be but it works. Also, when parsing the obtained string I had to put it in an exception because the first time a string is obtained it’s always gibberish:
dMeterReadingString = mmeter->ReadString();
firstChannelSTRING = dMeterReadingString->Subst ring(2, 5);
System::String *st = firstChannelSTRING;
try
{
dMeterReading = System::Double::Parse(st);
}
catch (Exception *exception){}
I wonder if that’s the best way to handle the problem but if you have any suggestions please let me know.
I still haven’t tried parsing the data for the second, third and fourth channel and it’s still unclear to me why should the data be obtained in such a strange format.
Of course, I got rid of the methods MultiMeter::Translate22812 (BYTE * bBuf), MultiMeter::ReadData() and MultiMeter::Process22812Re ad(BYTE * bBuf, DWORD dwRead) since they are not needed for the PIC device.
I had to place "COM1" instead of (LPCSTR)portName in
m_hComm = CreateFile( "COM1",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
In general the code works fine but there are some remaining issues. One of them is that the program responds very slowly to changes in the input voltage. If you remember I had a similar problem with the 22-812 multimeter when the interval was set to 1000ms:
aTimer = new System::Timers::Timer(1000 );
As a matter of fact I could never make the 22-812 work for that interval. It appears that 22-812 could only work at interval 500ms. In this case (with the PIC device) even at the 500ms interval there are problems. First, as I said, the device reacts very slowly to sudden changes in the input voltage – reaction is seen probably half a minute after the application of such sudden change. Second, when these changes start to occur the response is quite erratic. Only after removal of the "stress" the response follows a smooth curve. There may be some other small details which I should mention but I'll probably do that elsewhere.
Thanks again for the great help.
P.S. I may open a separate question to discuss the problems I mentioned above.
dwRead = ReadPort(buf, 27);
I don’t know why this should be but it works. Also, when parsing the obtained string I had to put it in an exception because the first time a string is obtained it’s always gibberish:
dMeterReadingString = mmeter->ReadString();
firstChannelSTRING = dMeterReadingString->Subst
System::String *st = firstChannelSTRING;
try
{
dMeterReading = System::Double::Parse(st);
}
catch (Exception *exception){}
I wonder if that’s the best way to handle the problem but if you have any suggestions please let me know.
I still haven’t tried parsing the data for the second, third and fourth channel and it’s still unclear to me why should the data be obtained in such a strange format.
Of course, I got rid of the methods MultiMeter::Translate22812
I had to place "COM1" instead of (LPCSTR)portName in
m_hComm = CreateFile( "COM1",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
In general the code works fine but there are some remaining issues. One of them is that the program responds very slowly to changes in the input voltage. If you remember I had a similar problem with the 22-812 multimeter when the interval was set to 1000ms:
aTimer = new System::Timers::Timer(1000
As a matter of fact I could never make the 22-812 work for that interval. It appears that 22-812 could only work at interval 500ms. In this case (with the PIC device) even at the 500ms interval there are problems. First, as I said, the device reacts very slowly to sudden changes in the input voltage – reaction is seen probably half a minute after the application of such sudden change. Second, when these changes start to occur the response is quite erratic. Only after removal of the "stress" the response follows a smooth curve. There may be some other small details which I should mention but I'll probably do that elsewhere.
Thanks again for the great help.
P.S. I may open a separate question to discuss the problems I mentioned above.
ASKER
Forgot to ask you something ... What is the method if one wants to write to the device, say, if one wants to set the output voltage to a certain value?
ASKER
I just posted a continuation of this question in:
https://www.experts-exchange.com/questions/21109044/Questions-to-drichards-regarding-the-managed-code-for-the-multimeter-Continued.html
https://www.experts-exchange.com/questions/21109044/Questions-to-drichards-regarding-the-managed-code-for-the-multimeter-Continued.html
If you want to write to the device, you use WriteFile:
BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped );
In terms of the MultiMeter class:
char buf[64]; // pick a size, or maybe you get data from somewhere else
// ... load buffer with data you want to write
int dataLen = <desired value>; // replace <desired value> with the number of data bytes you want to write
int numWritten = 0;
WriteFile(m_hComm, buf, dataLen, &numWritten, NULL);
BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped );
In terms of the MultiMeter class:
char buf[64]; // pick a size, or maybe you get data from somewhere else
// ... load buffer with data you want to write
int dataLen = <desired value>; // replace <desired value> with the number of data bytes you want to write
int numWritten = 0;
WriteFile(m_hComm, buf, dataLen, &numWritten, NULL);
ASKER
Thanks ... I haven't tried it yet but if I have questions when I try it I 'll open a new question.
>> #define SERIAL_READ_BUF_SIZE 4 * SERIAL_READ_PKT_SIZE
These are just defining packet sizes and were appropriate for the meter. You might just want to make these big (whatever that means for you - like 256 or 1024 bytes, for example) and not worry about it. These numbers are juts used to size data buffers.
As for the reading itself, just remove the call to 'Translate22812' and replace it with whatever procesing you need for this new device. If you have trouble with this, just give me the details of the data format and I'll tell you how to process it.
For the serial port setings, you can either code new values or add parameters to allow you to set values from the client code. You could also add a 'SetSerialParams' method or a new constructor and add some class members to hold the configured values.
Let me know if you need code samples.