?
Solved

Questions to drichards regarding the managed code for the multimeter

Posted on 2004-08-25
11
Medium Priority
?
316 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 4
11 Comments
 
LVL 19

Expert Comment

by:drichards
ID: 11896528
>> #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
ID: 11897422
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
ID: 11897626
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:judico
ID: 11897933
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
ID: 11897938
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
 
LVL 19

Accepted Solution

by:
drichards earned 2000 total points
ID: 11899559
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
ID: 11901038
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
ID: 11902025
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
ID: 11908126
0
 
LVL 19

Expert Comment

by:drichards
ID: 11919210
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
ID: 11919321
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

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
Suggested Courses

800 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