Solved

Questions to drichards regarding the managed code for the multimeter

Posted on 2004-08-25
11
302 Views
Last Modified: 2013-12-03
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.
0
Comment
Question by:judico
  • 7
  • 4
11 Comments
 
LVL 19

Expert Comment

by:drichards
Comment Utility
>> #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.
0
 

Author Comment

by:judico
Comment Utility
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?
0
 
LVL 19

Expert Comment

by:drichards
Comment Utility
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?
0
 

Author Comment

by:judico
Comment Utility
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.
0
 

Author Comment

by:judico
Comment Utility
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.
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 19

Accepted Solution

by:
drichards earned 500 total points
Comment Utility
I put a new zip file out for you: http://home.comcast.net/~n_a/MeterLib.zip

This is a dll project that has the MultiMeter class in it.  You can reference this project from any .NET language or you can just copy the implementation of MultiMeter into your existing C++ project over the current MultiMeter class.  This new one passes the COM port parameters through 'ConfigureSerialInterface' and has a new method called 'ReadString' that returns the serial data as a string object.  You can parse the returned string as necessary.

In VB.NET, you can use MultiMeter like this:

        Dim m As MeterLib.MultiMeter = New MeterLib.MultiMeter
        Dim pf As MeterLib.PASS_FAIL = m.ConfigureSerialInterface(1, Convert.ToUInt32(2400), 0, 7, 2)
        Dim res As String = m.ReadString()

In C++ you use it like before except with more parameters on ConfigureSerialInterface and the new ReadString method.
0
 

Author Comment

by:judico
Comment Utility
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.
0
 

Author Comment

by:judico
Comment Utility
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?
0
 

Author Comment

by:judico
Comment Utility
0
 
LVL 19

Expert Comment

by:drichards
Comment Utility
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);
0
 

Author Comment

by:judico
Comment Utility
Thanks ... I haven't tried it yet but if I have questions when I try it I 'll open a new question.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now