Instrument Control (GPIB, Serial, VISA, IVI)

cancel
Showing results for 
Search instead for 
Did you mean: 

How do I read from a serial 485 card in a C# program?

Hello,
I am going nuts trying to figure out how to communicate with my serial card.  I know about the visa library, but since I'm using C#, I can't really include the "visa.h" file in my program.  I've tried using the VisaCommLib dll library, but I get an incorrect data when I read from the serial port.  For instance, the scope shows that there are 8 bytes coming in, but my visa read function only gives me 5 bytes.  And sometimes the bytes read show up as some weird unicode characters instead of 8-bit Ascii characters.  I've included my code below.  Please help.
 

using Ivi.Visa.Interop;
using interop.visacomlib;
...
rm = new VisaComLib.ResourceManagerClass();
WriteSession = (VisaComLib.IMessage)rm.Open("ASRL3::INSTR",
               VisaComLib.AccessMode.NO_LOCK, 0, "");
ReadSession = (VisaComLib.IMessage)rm.Open("ASRL3::INSTR",
              VisaComLib.AccessMode.NO_LOCK, 0, "");
....
public string SerialIO_Read()
{
   string strRd = "";
   try
   {
      while(true)
      {
         strRd += ReadSession.ReadString(1);
   }
   }
   catch(System.Exception)
   {
      Console.WriteLine("Could not read from serial port.");
   }
   return strRd;
}
 
0 Kudos
Message 1 of 13
(12,784 Views)

First, you don't have to have two separate sessions for Write and Read.  Some session attributes are "Local Attributes" that are managed per session, thereby you may have to set the same attributes twice for two sessions for each.  Instead, having only one session simplies them.

Here's a VISA-COM C# example that just queries *IDN? response to an SCPI instrument, with 19200/N/8/1 and X-Flow.  (You may change serial communication conditions according to your actual instrument to connect.)

   IResourceManager rm = null;
   IMessage session = null;
   string strRd;
   System.Array block;

   try
   {
    rm = new ResourceManager();
    session = (IMessage)rm.Open("ASRL3::INSTR", AccessMode.NO_LOCK, 0, "");
    ISerial  ser = (ISerial)session;
    
    ser.BaudRate = 19200;
    ser.DataBits = 8;
    ser.StopBits = SerialStopBits.ASRL_STOP_ONE;
    ser.Parity = SerialParity.ASRL_PAR_NONE;
    ser.FlowControl = SerialFlowControl.ASRL_FLOW_XON_XOFF;

    // Read until LF(0x0A) byte for *IDN? response
    session.WriteString( "*IDN?\n");
    strRd = session.ReadString( 100);

    // Read exactly 8 bytes for *IDN? response
    // (with waiting for 8 bytes get available for read)
    session.TerminationCharacterEnabled = false;
    session.WriteString( "*IDN?\n");
    while( ser.BytesAvailable < 😎
    {
     ;
    }
    block = session.Read( 8);


   }
   catch( System.Runtime.InteropServices.COMException ex)
   {
    MessageBox.Show( ex.Message, "error 0x" + Convert.ToString(ex.ErrorCode, 16));
   }
   finally
   {
    session.Close();
   }

In the TRY block, there are two different approaches for reading, 1)typical ASCII read until LF(0x0A) terminate, 2)block read for specific byte length.  As for typical ASCII command IO bases, the 1st approcah is much easier.  As for arbitrary block read, you should disable the TerminationCharacterEnabled property to avoid unexpected termination for the case when an LF code was encountered.  Also you can check the byte length that is available in the read-buffer by using ISerial.BytesAvailable property before reading.

Hope this helps.
Makoto Kondo

0 Kudos
Message 2 of 13
(12,769 Views)
Dear Makoto,
 
Thank you very much, that is a big help.  HOwever, when I run the code, and after it sends the "*IDN?\n" string, and it tries to read from the serial port, I get an exception and the message box sais "Invalid aspect(s)".  Please tell me what I am doing wrong?  Also, what COM library do I need to reference in my project to be able to run this code correctly?  Right now I only have the Interop.VisaComLib Reference in my project.  Is that ok? Or do I need something else.  Thanks...
 
Goharik
0 Kudos
Message 3 of 13
(12,762 Views)
> Right now I only have the Interop.VisaComLib Reference in my project.  Is that ok?
 
It is enough to add reference to VISA COM Type Library (VisaComLib).  There is an optional component VISA COM 488.2 Formatted I/O, but it is not required unless you use formatted IO services.  Off course adding the reference to VISA COM 488.2 Formatted I/O is harmless so you may add it without problems.  If your program does use formatted IO services and VISA COM 488.2 Formatted I/O reference is missing, the C# compiler must claim a compilation error.
 
> I get an exception and the message box sais "Invalid aspect(s)". 
 
I have never seen this error when using VISA COM with .NET languages.  What hexadecimal error code did you see?  (You can check the error code by "ex.ErrorCode" in the CATCH block).  If the error code is from VISA COM library, it is a VISA-related problem including your RS-485 hardware and/or its configuration.  Otherwise, it may be a .NET/COM interop related problem.  I now googled the "Invalid aspect(s)" message but could not find any related topics.  Unfortunately I do not have RS-485 hardware and my serial port is the stadard COM1 (RS232).  I believe they must be the same in terms of VISA Serial programming though.
 
0 Kudos
Message 4 of 13
(12,754 Views)

I rand the program again, and got an exception when reading the response to the "IDN*"command: "Exception from HRESULT: 0x80040072".  Do you know what it means??  Maybe I have the session configured wrong?  I only used the same parameters that you had shown.

 

I have also incorportared the example code you gave me into my program, and I am still getting the same [incorrect] data when I communicate with my device.  For example, when I send a command to the device and read the response, the scope reads 8 bytes coming back in the response, but the VISA ReadString function only gives me 5 bytes.  And sometimes I get these characters with strange values that do not even map into ASCII characters, and when I convert the values to decimal values I get very high numbers, like 8322.  How can that be a single byte?

Here's some sample output from my program:

'DefaultDomain': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
'E2C_BI': Loaded 'C:\Software\E2C BI source\E2C_BI\bin\Debug\E2C_BI.exe', Symbols loaded.
'E2C_BI.exe': Loaded 'c:\windows\assembly\gac\system.windows.forms\1.0.5000.0__b77a5c561934e089\system.windows.forms.dll', No symbols loaded.
'E2C_BI.exe': Loaded 'c:\windows\assembly\gac\system\1.0.5000.0__b77a5c561934e089\system.dll', No symbols loaded.
'E2C_BI.exe': Loaded 'c:\software\e2c bi source\e2c_bi\bin\debug\interop.visacomlib.dll', No symbols loaded.
Exception from HRESULT: 0x80040015.
error 0x80040015

Exception from HRESULT: 0x80040015.
error 0x80040015
8222
Exception from HRESULT: 0x80040015.
error 0x80040015
402 70 2 74 1 1
Exception from HRESULT: 0x80040015.
error 0x80040015
8364
Exception from HRESULT: 0x80040015.
error 0x80040015
8364

0 Kudos
Message 5 of 13
(12,752 Views)

The error code 0x80040072 is probably from VISA-COM as it defines -- E_VISA_RSRC_BUSY (The resource is valid, but VISA cannot currently access it).  I have no experience to encounter this error code, however, some of hardware resource is conflicting by other VISA sessions (if any same or similar resource is being occupied).  That error is very VISA implementation specific and hardwware IO specific.  I recommend you to check if any other program has an access the same RS-485 port at the same time.

As for strange high byte characters, I believe ANSI-UNICODE conversion is concerning.  VISA-COM treats every STRING parameter and return as a UNICODE string.  This is a generic COM rule defined by Microsoft.  Therefore ReadString() method that returns BSTR (= W_CHAR* type) type internally converts ASCII based GPIB/Serial response string to UNICODE.  If the instrument response includes only ASCII characters below 0x7F, the ReadString() returns the same image without problem.  However, if the response includes a high-byte char, ANSI-UNICODE conversion misunderstands as if the given high-byte was a multi-byte portion of Asian SHIFT-JIS(Japan), GB2312(China), BIG-5(Taiwan), or KS(Korea) encodes  Therefore, if your instrument may return a response including one or more high-bytes, you should use Read() method rather than ReadString(). The VISA-COM IMessage::Read() method returns a BYTE array (strictly a COM-defined SAFEARRAY of unsigned char type) that has the exactly same contents as the instrument returned.  No UNICODE conversion is applied because the return type is BYTE array (not STRING !).  Through the .NET COM interop layer, the Read() method returns .NET System.Array type that will actually contain a BYTE array. 

As for unexpected termination, VISA's TerminationCharacterEnabled property may be activated.  The default of this is TRUE and the defalt TerminationCharacter applied is 0x0A.  Setting TerminationCharacterEnabled = false will avoid unexpected termination even if LF (0x0A) was sent from the instrument. 

0 Kudos
Message 6 of 13
(12,743 Views)

Hi Goharik,

I noticed from your posts here that you're using the VISA-COM API through interop in C#. National Instruments provides a full-featured native .NET API that ships with NI-VISA. If you have Measurement Studio installed, it will be automatically installed when you install NI-VISA. If you do not have Measurement Studio, you might need to explicitly enable installation of the .NET API. It should appear in the feature tree under the "Development Support" as ".NET Framework 1.1 Languages Support". This feature installs a set of .NET assemblies into the GAC, as well as into the "C:\Program Files\National Instruments\MeasurementStudioVS2003\DotNET\Assemblies\Current" directory. If you have Measurement Studio installed, you can use the Add/Remove Class Library Wizard to add references to the NI-VISA assemblies to your project. You might find that this .NET API works better than using the IVI-COM components through interop.

0 Kudos
Message 7 of 13
(12,726 Views)
Thank you, everyone, for your help. 
 
I still can't resolve the 0x80040072 error.  I am ceratin that no other programs on the computer are using the the serial port, but I still encounter that error every time I send the *IDN?  query and try to read the response.
 
However, the good news is that I started using the BytesAvailable property and reading the data in byteArray format (instead of the readString() function), and now I am finally getting good data from the serial port.  Thanks for that tip, Makoto.   You were right.  It turned out the readString() method was in fact treating the data as multi-byte unicode characters, since my data uses all eight bits to encode information.  So reading into a byte array seems to have done the trick.  I'm also getting the correct number of bytes as well.
 
As for the .NET Framework 1.1 Languages Support, I do not currently have that installed on my computer.  I will check the driver CD that came with my card to see if I can install it.   I do not have Measurement Studio or any other National Instruments software, except for the drivers that came with my serial card (which was purchased from NI).  I only have Visual Studio .NET.  I've got one question though... after I install this feature, how do I go about using it?  Is there a dll I can reference in my project? If so, what is the name?  And an example would really be appreciated.  Thank you again....
 
Goharik
0 Kudos
Message 8 of 13
(12,694 Views)
A good place to start would be with the serial shipping examples for .NET. They should have been installed if you had selected the .NET support for NI-VISA to C:\Program Files\National Instruments\MeasurementStudioVS2003\DotNET\Examples\Visa\.

If you don't see the MeasureementStudio folder then you might want to try and download the latest NI-VISA from NI's web site and then make sure you select the .NET support.

-Josh
0 Kudos
Message 9 of 13
(12,684 Views)
Yes, there is a DLL that you can reference in your project.  Reference visa32.dll which is located in your Windows system32 folder.

Allen
0 Kudos
Message 10 of 13
(12,666 Views)