Link to home
Start Free TrialLog in
Avatar of judico
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(ByVal 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.ToString)
       
        ‘ 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.Substring(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.
Avatar of drichards
drichards

>> #define SERIAL_READ_PKT_SIZE 9
>> #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.
Avatar of judico

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?
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?
Avatar of judico

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.
Avatar of judico

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
Avatar of drichards
drichards

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of judico

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->Substring(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::Process22812Read(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.
Avatar of judico

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?
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);
Avatar of judico

ASKER

Thanks ... I haven't tried it yet but if I have questions when I try it I 'll open a new question.