Link to home
Start Free TrialLog in
Avatar of mrdtn
mrdtn

asked on

Radio shack 22-812 multimeter:

Hi,

I'm writing a data logging application using a Radio Shack 22-812 multi-meter, and am looking for a description of the date format.  Basically, I have been able to determine from other web searches that the data is a dump of the LCD pixel map.  My own analysis shows that the dump appears to be 56 bytes worth of data, but I don't know what those bytes map to.  Furthermore, I'm not sure how to synchronize with the data stream -- I suspect that there is some part of the data "packet" that represents a sync pattern or code (??).

I'm hoping someone knows something or knows someone else who does.

mrdtn
ASKER CERTIFIED SOLUTION
Avatar of Moncapitaan
Moncapitaan

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 mrdtn
mrdtn

ASKER

I knew I should have just posted to EE in the first place!  The protocol reference is exactly what I needed.

I can't tell you how much of a load off that is for me!!  This has to work by the morning, and I have three other device interfaces to implement.  I was looking at hacking the protocol myself, based on the sparse info I had already gotten and/or figuring out what a cryptic VB script I found on the net was doing.  I prefer having the technical info spelled out in front of me.

Thanks!!

mrdtn
No Problem!!
Thanks for the points

Mcp
Can one find somewhere code in VB.NET for obtaining data from this RadioShack 22-812 Multimeter? I'll appreciate it if someone can help me in that. Thanks in advance.

judico
Avatar of mrdtn

ASKER

Hello,

I wrote code in C after obtaining the information posted here.  My code is non-comprehensive since it was targetted toward a time-critical deadline to interface with a thermocouple, although I plan to enhance it to process all display modes (currently I only work with voltage mode).  One thing to note is that the checksum value in the posted link is decimal 57 (not hex as indicated), in other words 0x39.

Please let me know if posting any of the code will help.  I am not fluent in VB.NET, so I would not be able to provide a translation.

Good luck,

mrdtn
Thank you for the reply. It will be very nice of you if you could post the code here, especially the part which takes care of the receiving the data from the device and turning it into actual numbers. I don't know C so I also will not be able to translate the code into VB.NET but maybe it would be helpful to have it as a hint when attempting to write it in VB.NET.

Best regards,

judico
Avatar of mrdtn

ASKER

Judico,

I have provided what I believe should set you in the right direction, even if it in the context of C.  I thought of asking you to post a seperate question in order for me to get points for what I have provided below.  Instead, I am posting this here free of "charge" and will leave it up to you to decide if this information is helpful to you, in which case I would certainly welcome a "Points For mrdtn" question, as I haven't had as much time lately to devote to this site . . . Good Luck and feel free to ask for further explanation.

mrdtn

. . .

In the main.cpp program, the main lines of interest to you would be:

            if (0 == ReadFile(hComm, lpBuf, SERIAL_READ_BUF_SIZE, &dwRead, NULL))
            {
                  log(plogfile, "error reading COM port");
            }
            else
            {
                  meter_reading = Process22812Read(bBuf, dwRead, hComm);
                  temperature = meter_reading * T_DEGC_PER_MV;
            }

where I obtain the temperature of the thermocouple based on the voltage reading of the meter.

I posted the entire main program for you to see how the serial port is configured.  Once configured, the "process22812read" function handles the processing of the serial data as it is read.  The trick is syncing up with the screen-dump boundary.  Each screen dump is grouped into 9 8-bit packets, as described in the link,

http://www.zworld.com/documentation/docs/refs/AN403/22-812protocol.pdf

Syncronizing with this data stream was done as follows (and is handled in function Process22812Read).  Although the terminology may be confusing, the macro variable SERIAL_READ_PKT_SIZE actually refers to the entire screen dump.  It's value of 9 represents 9 BYTES of data.  The variable SERIAL_READ_BUF_SIZE is defined as 8 times the packet size -- basically I am "over-sampling" the data stream.  What this does is cause the serial read in the main program to read 8 consequtive screen dumps-worth of data.  The function process22812read begins at the start of the data buffer which has just been read in and computes a checksum.  It then checks it to see if it matches the 0x39 (57 decimal) value specific to the 22812.  It continues this checksum computation/verification at the next byte in the buffer . . and then the next . . . until a checksum match is found . . . or until it has performed "SERIAL_READ_PKT_SIZE" computations, in which case there would be a problem since synchronization should occur in no fewer attempts, since the packet repeats.  When the checksum match occurs, a "break" statement occurs, which in C causes the for-loop to terminate.  Right after the for-loop mentioned above, there is

      if (i == SERIAL_READ_PKT_SIZE) . . . .

If this condition is true, the previous for-loop was not terminated early indicating that there was no checksum match.  This if-else performs two functions in my program: (1)keeping track of consequtive serial sync failures and terminating if a threshold is exceeded, and (2)translating the 1's and 0's from a successful meter reading into engineering values.  The latter function is handled in the function Translate22812, which is where I use the LCD segment mappings available in the URL above to form the result.  In the case of the voltage reading I am doing, I determine which of the segments A through G are "on" for each of the 4 segmented digits, d1 through d4.  Then I determine which decimal point segment is "on", p1, p2, or p3 (I also check to make sure that only one is on).  I also determine if the "neg" segment is displayed.  You see a pattern?  At some point in the future, I plan on re-writing this into a C++ class and providing a comprehensive processing of the remaining segments of the display.  However, for now, this is what I have got.  Keep in mind that there is some other stuff in the main function which involves some other devices, which you can basically ignore.  I hope it's not too confusing.  If you know someone who knows C, they should be able to bridge the information gap you may have with the syntax here.

I hope this has been helpful.

--
mrdtn





/****************************** Begin of File global_variables.h **************************/

#ifndef GLOBAL_VARIABLES_H
#define GLOBAL_VARIABLES_H
//
#ifdef GLOBAL_ALLOCATE
      #define GLOBAL
#else
      #define GLOBAL extern
#endif
//
GLOBAL BOOL bRun;
GLOBAL BOOL serial_stream_in_sync;
GLOBAL int ps33;
GLOBAL int ps18;
GLOBAL int ps50;
GLOBAL int ps28;
GLOBAL int ps[4];
GLOBAL FILE * plogfile;
//
#end

/****************************** End of File global_variables.h **************************/


/****************************** Begin of File parameters.h **************************/

#ifndef PARAMETERS_H
#define PARAMETERS_H
//
#define SERIAL_READ_PKT_SIZE 9
#define SERIAL_READ_BUF_SIZE 8 * SERIAL_READ_PKT_SIZE
#define SER_SYNC_LOSS_THRESH 30
#define GPIB_READ_BUFFER_SIZE 100
#define TIME_TAG_FORMAT_STRING "yyyy.mm.dd.hh.mm.ss"
//
#define CHECKSUM_22812 0x39
//
#define T_DEGC_PER_MV 1.0
//
#define T_THRESH_MAX 55.0
#define T_THRESH_MIN -55.0
//
#define P28_MIN_VOLTAGE 27.9
#define P28_MAX_VOLTAGE 28.2
#define P28_MIN_CURRENT -0.2
#define P28_MAX_CURRENT 0.2
//
#define P50_MIN_VOLTAGE 4.9
#define P50_MAX_VOLTAGE 5.2
#define P50_MIN_CURRENT -0.05
#define P50_MAX_CURRENT 0.05
//
#define P33_MIN_VOLTAGE 3.3
#define P33_MAX_VOLTAGE 3.5
#define P33_MIN_CURRENT -1.3
#define P33_MAX_CURRENT 1.3
//
#define P18_MIN_VOLTAGE 1.6
#define P18_MAX_VOLTAGE 1.8
#define P18_MIN_CURRENT -4.9
#define P18_MAX_CURRENT 4.9
//
#define LOGFILE FALSE // for testing
#define GPIB FALSE // for testing
//
typedef enum {PASS, FAIL} PASS_FAIL;
//
#define NUM_POWER_SUPPLIES 4
typedef enum {PS_1_5_VOLT, PS_NEG_5_VOLT, PS_3_3_VOLT, PS_28_VOLT} POWER_SUPPLY;
//
#define _30_MINUTES 60 * 30
#define _5_MINUTES 60 * 5
//
#endif

/****************************** End of File parameters.h **************************/

/****************************** Begin of File main.cpp **************************/

// mon.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
//
#include "afx.h"
#include "decl-32.h"
#define GLOBAL_ALLOCATE
      #include "global_variables.h"
#undef GLOBAL_ALLOCATE
#include <conio.h>
#include "parameters.h"
#include "function_templates.h"
//
int main(int argc, char* argv[])
{
      // serial COM variables
      HANDLE hComm;
      DWORD dwRead;
      long lpBuf[SERIAL_READ_BUF_SIZE];
      BYTE * bBuf = (BYTE *)(lpBuf);
      // global initializations
      bRun = TRUE;
      serial_stream_in_sync = FALSE;
      // instrumentation variables
      double temperature = 0.0;
      double voltage[NUM_POWER_SUPPLIES];
      double current[NUM_POWER_SUPPLIES];
      double v28 = 0.0, v50 = 0.0, v33 = 0.0, v18 = 0.0;
      double i28 = 0.0, i50 = 0.0, i33 = 0.0, i18 = 0.0;
      double meter_reading;
      // setup control handler for application
    if (!SetConsoleCtrlHandler (ctrl_c_handler, TRUE)) printf("\nSetConsoleCtrlHandler failed.");
      // open log file
      if (FAIL == OpenLogFile()) {printf("\nError opening log file."); return 0;}
      // initialize GPIB devices
      GpibInitialize();
      // configure COM port for interfacing to 22-812 DMM
      if (FAIL == ConfigureSerialInterface(hComm)) {printf("\nError configuring serial port."); return 0;}
      // main loop
      while (TRUE == bRun)
      {
            //
            static time_t run_epoch;
            static time_t fail_epoch;
            //
            static unsigned int failure_count = 0;
            static time_t failure_count_epoch;
            //
            char time_tag[] = TIME_TAG_FORMAT_STRING;
            //
            time_t utc;
            time(&utc);
            printf("\nSystem Time = %d", utc);
            //
            if (_kbhit())
            {
                  char ch = _getch();
                  switch (ch)
                  {
                        case 'x': case 'X': break;
                        default: break;
                  }
            }
            //
            GetTimeTag(time_tag);
            //
            if (GPIB) for (int i = 0; i < NUM_POWER_SUPPLIES; i++)
            {
                  ReadPowerSupply(ps[i], voltage[i], current[i]);
            }
            //
            if (0 == ReadFile(hComm, lpBuf, SERIAL_READ_BUF_SIZE, &dwRead, NULL))
            {
                  log(plogfile, "error reading COM port");
            }
            else
            {
                  meter_reading = Process22812Read(bBuf, dwRead, hComm);
                  temperature = meter_reading * T_DEGC_PER_MV;
            }
            //
            char logmsg[256];
            sprintf(logmsg, "TC=%-10.6f V28=%-10.7f I28=%-10.7f V50=%-10.7f I50=%-10.7f V33=%-10.7f I33=%-10.7f V18=%-10.7f I18=%-10.7f ", temperature, v28, i28, v50, i50, v33, i33, v18, i18);
            log(plogfile, logmsg);
            //
            if (GPIB)
            {
                  if (FAIL == CheckConstraints(v28, v50, v33, v18, i28, i50, i33, i18, temperature))
                  {
                        log(plogfile, "Constraint Check Failed");
                        PerformShutdownSequence();
                        log(plogfile, "Log File Closing");
                        fclose(plogfile);
                        bRun = FALSE;
                  }
            }
      }
      return 0;
}

// Keyboard interrupt handler.
static BOOL WINAPI ctrl_c_handler (DWORD type)
{
      return TRUE;  // Notify not to call other handlers.
}

/****************************** End of File main.cpp **************************/


/****************************** Begin of File serial.cpp **************************/

#include "stdafx.h"
//
#include "parameters.h"
#include "global_variables.h"
#include "function_templates.h"
//
PASS_FAIL ConfigureSerialInterface(HANDLE &hComm)
{
      // 22-812 serial rs-232 interface code . . .
      //
      DCB config;
      //
      // open serial port
      //
      hComm = CreateFile(      "COM1",
                                    GENERIC_READ | GENERIC_WRITE,
                                    0,
                                    NULL,
                                    OPEN_EXISTING,
                                    0,
                                    NULL);
      if (hComm == INVALID_HANDLE_VALUE)
      {
            log(plogfile, "Could not COM port (error)");
            return FAIL;
      }

      //
      // get port state and store in DCB
      //
      if (0 == GetCommState(hComm, &config))
      {
            log(plogfile, "Error in getcommstate");
            return FAIL;
      }

      //
      // configure port
      //
      config.BaudRate = CBR_4800;
      config.Parity = NOPARITY;
      config.ByteSize = 8;
      config.StopBits = ONESTOPBIT;
      //
      if (0 == SetCommState(hComm, &config))
      {
            log(plogfile, "Error in setcommstate");
            return FAIL;
      }

      //
      // set timeouts
      //
    COMMTIMEOUTS comTimeOut;                  
    comTimeOut.ReadIntervalTimeout = 0;
    comTimeOut.ReadTotalTimeoutMultiplier = 0;
    comTimeOut.ReadTotalTimeoutConstant = 2500;
    comTimeOut.WriteTotalTimeoutMultiplier = 3;
    comTimeOut.WriteTotalTimeoutConstant = 2;
      //
    SetCommTimeouts(hComm,&comTimeOut);
      //
      return PASS;
}

/****************************** End of File serial.cpp **************************/


/****************************** Begin of File process22812read.cpp **************************/

#include "stdafx.h"
//
#include "parameters.h"
#include "function_templates.h"
#include "global_variables.h"
//
double Process22812Read(BYTE * bBuf, DWORD dwRead, HANDLE &hComm)
{
      static int serial_stream_out_sync_count = 0;
      unsigned int checksum = 0;
      if (dwRead < 1) return 0.0;
      unsigned int i;

      for (i = 0; i < SERIAL_READ_PKT_SIZE; i++)
      {
            checksum = 0;
            for (unsigned int j = i; j < i + SERIAL_READ_PKT_SIZE + 1; j++)
            {
                  checksum += bBuf[j];
            }
            checksum += CHECKSUM_22812;
            checksum &= 0xff;
            if (checksum == bBuf[i + SERIAL_READ_PKT_SIZE + 1]) break;
      }
      if (i == SERIAL_READ_PKT_SIZE)
      {
            int msgptr = 0;
            char msg[256];
            serial_stream_in_sync = FALSE;
            msgptr += sprintf(msg, "22-812 checksum failure");
            for (int ctr = 0; ctr < SERIAL_READ_BUF_SIZE; ctr++)
            {
                  msgptr += sprintf(msg + msgptr, "% 02x", bBuf[ctr]);
            }
            log(plogfile, msg);
            serial_stream_out_sync_count++;
            if (serial_stream_out_sync_count > SER_SYNC_LOSS_THRESH)
            {
                  serial_stream_out_sync_count = 0;
                  log(plogfile, "22-812 Unable to synchronize with 22-812 data stream");
                  PerformShutdownSequence();
            }
            return 0.0;
      }
      else
      {
            serial_stream_in_sync = TRUE;
            serial_stream_out_sync_count = 0;
            double meter = Translate22812(&bBuf[i]);
            return meter;
      }
}

/****************************** End of File process22812read.cpp **************************/


/****************************** Begin of File translate_meter.cpp **************************/

#include "stdafx.h"
#include "decl-32.h"
#include <windows.h>
#include "parameters.h"
#include "function_templates.h"
#include "global_variables.h"

double Translate22812(BYTE * bBuf)
{
      unsigned int d1, d2, d3, d4;
      unsigned int p1, p2, p3;
      unsigned int neg;
      
      switch (bBuf[5] & 0x08)
      {
            case 0x08: p1 = 1; break;
            case 0x00: p1 = 0; break;
      }
      switch (bBuf[4] & 0x08)
      {
            case 0x08: p2 = 1; break;
            case 0x00: p2 = 0; break;
      }
      switch (bBuf[3] & 0x08)
      {
            case 0x08: p3 = 1; break;
            case 0x00: p3 = 0; break;
      }
      switch (bBuf[7] & 0x08)
      {
            case 0x08: neg = 1; break;
            case 0x00: neg = 0; break;
      }
      switch (bBuf[6] & 0xf7)
      {
            case 0xd7: d1 = 0; break;
            case 0x50: d1 = 1; break;
            case 0xb5: d1 = 2; break;
            case 0xf1: d1 = 3; break;
            case 0x72: d1 = 4; break;
            case 0xe3: d1 = 5; break;
            case 0xe7: d1 = 6; break;
            case 0x51: d1 = 7; break;
            case 0xf7: d1 = 8; break;
            case 0xf3: d1 = 9; break;
            default: log(plogfile, "22-812 meter translation error"); PerformShutdownSequence(); break;
      }
      switch (bBuf[5] & 0xf7)
      {
            case 0xd7: d2 = 0; break;
            case 0x50: d2 = 1; break;
            case 0xb5: d2 = 2; break;
            case 0xf1: d2 = 3; break;
            case 0x72: d2 = 4; break;
            case 0xe3: d2 = 5; break;
            case 0xe7: d2 = 6; break;
            case 0x51: d2 = 7; break;
            case 0xf7: d2 = 8; break;
            case 0xf3: d2 = 9; break;
            default: log(plogfile, "22-812 meter translation error"); PerformShutdownSequence(); break;
      }
      switch (bBuf[4] & 0xf7)
      {
            case 0xd7: d3 = 0; break;
            case 0x50: d3 = 1; break;
            case 0xb5: d3 = 2; break;
            case 0xf1: d3 = 3; break;
            case 0x72: d3 = 4; break;
            case 0xe3: d3 = 5; break;
            case 0xe7: d3 = 6; break;
            case 0x51: d3 = 7; break;
            case 0xf7: d3 = 8; break;
            case 0xf3: d3 = 9; break;
            default: log(plogfile, "22-812 meter translation error"); PerformShutdownSequence(); break;
      }
      switch (bBuf[3] & 0xf7)
      {
            case 0xd7: d4 = 0; break;
            case 0x50: d4 = 1; break;
            case 0xb5: d4 = 2; break;
            case 0xf1: d4 = 3; break;
            case 0x72: d4 = 4; break;
            case 0xe3: d4 = 5; break;
            case 0xe7: d4 = 6; break;
            case 0x51: d4 = 7; break;
            case 0xf7: d4 = 8; break;
            case 0xf3: d4 = 9; break;
            default: log(plogfile, "22-812 meter translation error"); PerformShutdownSequence(); break;
      }

      if (p1 + p2 + p3 > 1)
      {
            log(plogfile, "22-812 meter translation error");
            PerformShutdownSequence();
      }

      double return_value = d1 * 1000.0 + d2 * 100.0 + d3 * 10.0 + d4;
      //
      if (1 == p1) return_value /= 1000.0;
      else if (1 == p2) return_value /= 100.0;
      else if (1 == p3) return_value /= 10.0;
      //
      if (1 == neg) return_value = - return_value;

      return return_value;
}

/****************************** End of File translate_meter.cpp **************************/
Avatar of mrdtn

ASKER

One more thing:  I over-sampled the data  (8x) based primarily on analysis of the data stream rate coming from the meter.  There are more eligant approaches to ensuring that no data is lost, or on the other hand flushing the buffer each time, but since my monitoring program only needed to sample the data every 1-2 seconds, and I wasn't worried about losing a screen sample, and because I was in a bit of a hurry to get this done over a weekend, I chose 8x over-sampling to ensure that I was reading the data quickly enough.  If data isn't flushed from the I/O buffer or if it is not read quickly enough to essentially give the output device (the 22812) "flow control", the data will get buffered up by the I/O system and the data you read will not be current, since data is put on the buffer in a "first-in-first-out" manner, and the data read out is always the oldest data still in the buffer.

mrdtn
mrdtn,

It is very much appreciated that you submitted the above code. Just for submitting it I think you deserve points. Please advise what I need to do to send you points so that you'll get them.

Once I determine if it works for me then I will send you many more points since it will be very helpful.

Could you please tell me if you can translate above code into Visual C++ 2003? I don't know that language yet but if you translate the above code into it I will specially devote time to learning it.

Thanks againg for posting the code.

judico
Avatar of mrdtn

ASKER

Judico,

The code above WAS done using Visual Studio / C++ version 6.0.  I WILL work with Visual C++ 2003.  I don't have time right this minute, but I can easily gut out  the unnecessary code and package it up and put it on my web site or something later today/tomorrow -- posting email addresses is against EE rules, so that's not an option.

As far as points go, you can either post your original post for this question as a completely seperate question, or since we have already continued to add the additional information to this existing question, what is customarily done in such a situation is to post a question with subject "Points for <name>" in the same topic area referring to the URL of this question and then adding a post to this question with a link to the "Poinjts for" question (in order that the person's inbox gets "pinged").

Anyway, as the originator of this question, I was grateful for the accepted answer, and sharing your desire to interface with the 22812 device, I am happy to share the knowledge.

Anyway, check later for posts.

mrdtn
Avatar of mrdtn

ASKER

One thing to clarify:

The fact that I stated that my code was written in C and the fact that I used Visual C++ should not be a source for confusion.  C++ is really a super-set of the C language.  There are some considerations when mixing certain C and C++ libraries, but generally speaking, C code can be compiled and linked using either a C or C++ compiler.  You should be able to paste the above code into Visual C++ 2003 and have things compile.  What I will post will be a complete "workspace" which will build a standalone executable which you can then use to learn from and enhance at will to add more comprehensive functionality for interfacing with the 22812.

mrdtn
mrdtn.

Please take a look at:

https://www.experts-exchange.com/questions/21070426/Points-for-mrdtn.html

where I gave you 500 points. I am looking forward to seeng the code you mentioned you will post on your website. How would I access your website, though? Thanks again for your willingness to help.

judico
Avatar of mrdtn

ASKER

Judico,

Click on the URL below to download the zip file of the 22812 Visual C++ workspace:

http://members.verizon.net/~vze1wo72/ee/22812.zip

Unzip it to a folder.  In the new folder created, double-click on "mon.dsw" (you have to have Visual C++ installed).  THe project will open.  Build the project (F7 on my version) and run it (CTRL-F5).  Make sure you are connected to the 22812 and that you have put the meter in RS-232 mode (by pressing the "SELECT" and "RANGE" buttons together).

The program will run in "debug" mode by default.  At some point when you want to create a standalone application, you will want to create a "release" build.  You either already know that or will learn such things after a little familiarity with the environment.

The program runs in a console (DOS) window -- nothing fancy . . . just something to get the job done.  Log output of the meter reading with date/time tags as well as UTC are output to the screen as well as to a file having the date/time tag of the time it was created.  Control-C and break will not stop the program.  You must press the "x" key.

If I get a chance to enhance the functionality, I'll post update info.  Anyway, the zipped package will get you talking to your 22812 in about 30 seconds.

Good luck!

mrdtn
mrdtn,

Thank you very much for the code. It works just fine. What remains is for me to learn Visual C++ .NET and implement your code. I am giving you more point (500) for being so helpful. Please take a look at:

https://www.experts-exchange.com/questions/21070654/Points-for-mrdtn.html 

judico
Avatar of mrdtn

ASKER

Thanks again!!  Don't forget to close out the "Points For" questions by accepting my replies.  I knew the effort writing the code would somday benefit someone else.

mrdtn
Avatar of mrdtn

ASKER

Judico,

I updated the zip file on my web site.  After thinking a bit more on the serial interface and the suggested alternative of flushing the buffer each time, I modified the code to do just that.  With this change, I have changed the serial buffer size read in each loop to twice the screen buffer size to ensure that a complete screen sample exists in each buffer read regardless of buffer boundaries.  This approach is more sound and provides a slightly more responsive program while continuing to eliminate any latency which would be associated with not reading the incoming data stream fast enough.

mrdtn
mrdtn,

Would it be possible to redo the 22812 example in terms of managed code? I would like to obtain the value of the multimeter reading (in this example it is meter_value) in a form which contains the function:

protected:
      void OnPaint( PaintEventArgs *paintEvent )
{

graphicsObject->DrawEllipse( pen, count, meter_value, 7, 7 );

}

I do get the value of the reading in your console application. However, I am unable to use it in an actual Windows form. It would be of great help if you could show me how to do that. Thanks in advance.

judico
Avatar of mrdtn

ASKER

Sure,

I will post an example later which you should be able to taylor appropriately to your application.

mrdtn
Avatar of mrdtn

ASKER

I have posted a windows dialog application which encapsulates the code used before for the console program.  Download

http://members.verizon.net/~vze1wo72/ee/win22812.zip

This should provide enough insight as to how to incorporate my original code into a managed Windows application.

mrdtn
mrdtn,

Thanks for the new version of your code. Unfortunately, it still doesn’t seem to solve the problem. When trying to add a new Windows Form (.NET) to the project it gives the following error:

Managed components can only be added to managed projects. Please convert the project to managed.

I need the new Windows Form (.NET) to write the main part of my application in it. I tried to convert the project to managed following the instructions given in:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmex/html/vcconAddingMCToExistingApplication.asp

However, it turned out that when following the required sequence [target project->Properties->C/C++->General] it does not lead to a property ‘Compile As Managed’, as the instruction requires. There is a ‘Compile As’ (not  ‘Compile As Managed’) property when following the sequence [target project->Properties->C/C++->Advanced] but it was not possible to change it to ‘Assembly Support (/clr)’, as the instruction requires. Is there anything else that should be done to convert your original code into a managed Windows application?
Avatar of mrdtn

ASKER

Sorry I haven't gotten back sooner.  I have been sick for the past few days.

Unfortunately, I do not have Visual Studio .NET, and as far as I can tell, the Visual Studio version (6.0) I do have does not offer any options for creating a "managed" project.

Two suggestions:

1) If you haven't already, try creating a fresh project and try making it "managed" according to the link you provided.  Then, one-by-one, add the cpp modules from my project.  What I am thinking is that if you are starting with my original project file (.dsw), there may be issues converting an old project into a newer managed one.  As far as the code goes, it's pretty straight-forward and I would not expect such issues with the code itself.

2) Explore the .NET TA on EE (https://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/).

I'm sorry that I can't be of more help at this time.  Good luck and let me know if there is anything else I can (hopefully) help with.

mrdtn
mrdtn,

I am very sorry to hear that you are sick and hope you will get better very soon. You have been very helpful and I will try to follow the advice you gave me in your last posting. I will let you know if I had made any progress and will take the liberty to ask questions if such arise in the meantime. Wishes for speedy recovery.

judico
mrdtn,

Hope you are feeling better and if it is OK I'd like to ask you a question. I started transferring your code into a managed environment, got rid of the MFC includes but now I'm having problem with the template header. It appears that BYTE, DWORD and bBuf are defined in function_templates.h

#ifndef FUNCTION_TEMPLATES_H
#define FUNCTION_TEMPLATES_H
//
#include "parameters.h"
//
double Process22812Read(BYTE * bBuf, DWORD dwRead, HANDLE &hComm);
double Translate22812(BYTE * bBuf);
PASS_FAIL ConfigureSerialInterface(HANDLE &hComm);
//
#endif

but four of the .cpp files give me an error ‘undeclared identifier’. How can this error be corrected?

Avatar of mrdtn

ASKER

Judico,

Thanks . . and sure:

Actually, BYTE and DWORD are not defined in function_templates.h.  They are refererred to (referenced).  These definitions are implicitly defined somewhere.  bBuf is actually a dummy variable in the function template definition and can actually be omitted entirely.  I think you meant to say HANDLE instead.

Anyway, BYTE and DWORD are in the Microsoft header file WINDEF.h.  HANDLE is in WINNT.H.  If the .NET framework stil uses STDAFX.h, I would suggest placing the following lines in the STDAFX.H file:

#include WINDEF.H
#include WINNT.H

--

Otherwise, try placing the above lines in the CPP files which are complaining.

The "undeclared identifier" error is simple to resolve.  It just requires that the source code files include the appropriate header file or files which include the definition for the unresolved names.  At least with the developer studio, Microsoft has attempted to improve the experience of software development by using "pre-compiled" header files (hence the STDAFX.H stuff???).

Anyway, I hope this helps.

mrdtn
Thanks for the reply. I tried placing

#include WINDEF.H
#include WINNT.H

first in the STDAFX.H file. Alternatively, I placed them in the complaining .cpp files. In all cases I got the error messages:

error C2006: '#include' : expected a filename, found 'identifier'
fatal error C1083: Cannot open include file: '': No such file or directory

Probably the managed environment doesn't use STDAFX.h any more.

Avatar of mrdtn

ASKER

What was I thinking?  Put double-quotes around the filenames:

#include "WINDEF.H"
#include "WINNT.H"

mrdtn
Still doesn't accept it. Gives me:

error C2146: syntax error : missing ';' before identifier 'ContextRecord'
error C2501: '_EXCEPTION_POINTERS::PCONTEXT' : missing storage-class or type specifiers
error C2501: '_EXCEPTION_POINTERS::ContextRecord' : missing storage-class or type specifiers

in WinNT.h.
Avatar of mrdtn

ASKER

Here's an alternative to using the include statements for winnt.h and windef.h.  I'm not sure where these are declared in the .NET environment.  I'm not sure it's wise to explicitly include the header files I had suggested.  Explicitly defining these type definitions should get you past this hump:

Remove the include statements you added and add the definitions to function_templates.h so the file looks as shown below:

#ifndef FUNCTION_TEMPLATES_H
#define FUNCTION_TEMPLATES_H
//
#include "parameters.h"
//
typedef unsigned long       DWORD;
typedef unsigned char       BYTE;
typedef void *HANDLE;
//
double Process22812Read(BYTE * bBuf, DWORD dwRead, HANDLE &hComm);
double Translate22812(BYTE * bBuf);
PASS_FAIL ConfigureSerialInterface(HANDLE &hComm);
//
#endif
This replacement didn't work either. I found a text file in the Microsoft Visual Studio .NET 2003 folder, entitled migration_guide.doc. In it I read the following:

"Marshaling is required for types that do not have the same form. This includes char, string, and struct types. The following table shows the mappings used by the marshaler for various types."

and there is a table of the mentioned types wherein one sees the types we discuss here as 3 of the 17 types mentioned:

wtypes.h           C++                   Managed Extensions             Common Language Runtime

HANDLE            Void *                Void *                                  IntPtr, UIntPtr
BYTE                  unsigned char      unsigned char                         Byte
DWORD               unsigned long          unsigned long                      UInt32

I tried replacing HANDLE, BYTE and DWORD by UIntPtr, Byte and UInt32 but that gave me even more errors.
Thanks a lot for posting the question. Let's hope that someone will be able to help.
You should be able to fix those errors by using

    #include <windows.h>

rather than trying to include winnt.h and windef.h.

How are you accessing the serial port in .NET land if you are converting to a managed app?
Avatar of mrdtn

ASKER

Essentially, this way:

            FlushFileBuffers( m_hComm );
            
            //
            if (0 != ReadFile( m_hComm, m_lpBuf, SERIAL_READ_BUF_SIZE, &m_dwRead, NULL ) )
            {
                  pBuf = (BYTE *)m_lpBuf;
                  dMeterReading = Process22812Read( pBuf, m_dwRead, m_hComm );

                  strValue.Format( "%-10.6f", dMeterReading );
                  m_currentValueEdit.SetWindowText( strValue );
            }


-------------------------------------------------------------------------
A call to ConfigureSerialInterface (below) is made during initiialization.  You can download the project from a previous post.
-------------------------------------------------------------------------
#include "stdafx.h"
//
#include "parameters.h"
#include "function_templates.h"
//
PASS_FAIL ConfigureSerialInterface(HANDLE &hComm)
{
      // 22-812 serial rs-232 interface code . . .
      //
      DCB config;
      //
      // open serial port
      //
      hComm = CreateFile(      "COM1",
                                    GENERIC_READ | GENERIC_WRITE,
                                    0,
                                    NULL,
                                    OPEN_EXISTING,
                                    0,
                                    NULL);
      if (hComm == INVALID_HANDLE_VALUE)
      {
            //log(plogfile, "Could not COM port (error)");
            return FAIL;
      }

      //
      // get port state and store in DCB
      //
      if (0 == GetCommState(hComm, &config))
      {
            //log(plogfile, "Error in getcommstate");
            return FAIL;
      }

      //
      // configure port
      //
      config.BaudRate = CBR_4800;
      config.Parity = NOPARITY;
      config.ByteSize = 8;
      config.StopBits = ONESTOPBIT;
      //
      if (0 == SetCommState(hComm, &config))
      {
            //log(plogfile, "Error in setcommstate");
            return FAIL;
      }

      //
      // set timeouts
      //
    COMMTIMEOUTS comTimeOut;                  
    comTimeOut.ReadIntervalTimeout = 0;
    comTimeOut.ReadTotalTimeoutMultiplier = 0;
    comTimeOut.ReadTotalTimeoutConstant = 2500;
    comTimeOut.WriteTotalTimeoutMultiplier = 3;
    comTimeOut.WriteTotalTimeoutConstant = 2;
      //
    SetCommTimeouts(hComm,&comTimeOut);
      //
      return PASS;
}
I was just about to post this message and I saw drichards' message. I was going to say that I included

#include "windows.h"

and remmed out several lines (I don't know how important they are but first I want to see everything compile right) and could boil down the response to two error messages:

fatal error C1189: #error :  include 'stdafx.h' before including this file for PCH
fatal error C1189: #error :  include 'stdafx.h' before including this file for PCH

connected with

#ifndef __AFXWIN_H__
        #error include 'stdafx.h' before including this file for PCH
#endif
You need to delete the "#ifndef __AFXWIN_H__ ..." section because you are no longer using MFC and so are not using afxwin.h.  You still should include stdafx.h before any other file, however.

Also, if you use the serial port like that you are not doing a truly managed app.  You are using the IJW (it just works) feature of C++ .NET and going back and forth between managed code and unmanaged code.
Avatar of mrdtn

ASKER

Any suggestions on the equivalent means to do the serial interface in a "managed" manner are welcome.

Thanks,

mrdtn
I was trying that but removing #ifndef __AFXWIN_H__  section gave me 64 errors. For some reason the program needs it.
>> Any suggestions on the equivalent means to do the serial interface in a "managed" manner are welcome.

Unfortunately, there's no really good way to do it.  MsComm control works if your needs are not too demanding.  Here's an old question with some sugestions: https://www.experts-exchange.com/questions/20530084/VB-NET-read-from-COM-port.html.  Don't be put off that it was a VB.NET question - same answer holds for managed C++ or C#.

Best advice is just keep all the serial port code localized no matter what approach you use.  Then you can change it relatively easily if you buy a component or MS adds serial port access to .NET later on.
What types of errors do you get when you remove that?  Whatever file that was in must still be using some MFC stuff.  I'll get the code and have a look if you haven't resolved it by tomorrow.  Time to quit for the day.
These are the errors before removing
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif:

error C3861: 'sprintf': identifier not found, even with argument-dependent lookup
error C3861: 'sprintf': identifier not found, even with argument-dependent lookup
error C3861: 'AfxMessageBox': identifier not found, even with argument-dependent lookup
fatal error C1189: #error :  include 'stdafx.h' before including this file for PCH
fatal error C1189: #error :  include 'stdafx.h' before including this file for PCH

and these are the errors after removing it:

process22812read.cpp(27) : error C3861: 'sprintf': identifier not found, even with argument-dependent lookup
process22812read.cpp(30) : error C3861: 'sprintf': identifier not found, even with argument-dependent lookup
process22812read.cpp(34) : error C3861: 'AfxMessageBox': identifier not found, even with argument-dependent lookup
MultimeterDlg.cpp
Multimeter.h(25) : error C2504: 'CWinApp' : base class undefined
Multimeter.h(41) : error C2143: syntax error : missing ';' before '}'
Multimeter.h(41) : warning C4183: 'DECLARE_MESSAGE_MAP': missing return type; assumed to be a member function returning 'int'
MultimeterDlg.h(17) : error C2504: 'CDialog' : base class undefined
MultimeterDlg.h(20) : error C2143: syntax error : missing ')' before '*'
MultimeterDlg.h(20) : error C2143: syntax error : missing ';' before '*'
MultimeterDlg.h(20) : error C2460: 'CMultimeterDlg::CWnd' : uses 'CMultimeterDlg', which is being defined
MultimeterDlg.h(16) : see declaration of 'CMultimeterDlg'
MultimeterDlg.h(20) : error C2059: syntax error : ')'
MultimeterDlg.h(20) : error C2864: 'pParent' : only const static integral data members can be initialized inside a class or struct
MultimeterDlg.h(20) : error C2501: 'CMultimeterDlg::pParent' : missing storage-class or type specifiers
MultimeterDlg.h(25) : error C2146: syntax error : missing ';' before identifier 'm_currentValueEdit'
MultimeterDlg.h(25) : error C2501: 'CMultimeterDlg::CEdit' : missing storage-class or type specifiers
MultimeterDlg.h(25) : error C2501: 'CMultimeterDlg::m_currentValueEdit' : missing storage-class or type specifiers
MultimeterDlg.h(31) : error C2061: syntax error : identifier 'CDataExchange'
MultimeterDlg.h(45) : error C2144: syntax error : 'void' should be preceded by ';'
MultimeterDlg.h(45) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(46) : error C2144: syntax error : 'void' should be preceded by ';'
MultimeterDlg.h(46) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(47) : error C2146: syntax error : missing ';' before identifier 'HCURSOR'
MultimeterDlg.h(47) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(48) : error C2144: syntax error : 'void' should be preceded by ';'
MultimeterDlg.h(48) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(49) : error C2144: syntax error : 'void' should be preceded by ';'
MultimeterDlg.h(49) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(52) : error C2143: syntax error : missing ';' before '}'
MultimeterDlg.h(52) : warning C4183: 'DECLARE_MESSAGE_MAP': missing return type; assumed to be a member function returning 'int'
MultimeterDlg.cpp(28) : error C2504: 'CDialog' : base class undefined
MultimeterDlg.cpp(40) : error C2061: syntax error : identifier 'CDataExchange'
MultimeterDlg.cpp(48) : error C2143: syntax error : missing ';' before '}'
MultimeterDlg.cpp(48) : warning C4183: 'DECLARE_MESSAGE_MAP': missing return type; assumed to be a member function returning 'int'
MultimeterDlg.cpp(51) : error C2587: 'this' : illegal use of local variable as default parameter
        MultimeterDlg.cpp(50) : see declaration of 'this'
MultimeterDlg.cpp(51) : error C2614: 'CAboutDlg' : illegal member initialization: 'CDialog' is not a base or member
MultimeterDlg.cpp(51) : error C2587: 'this' : illegal use of local variable as default parameter
        MultimeterDlg.cpp(50) : see declaration of 'this'
MultimeterDlg.cpp(51) : fatal error C1903: unable to recover from previous error(s); stopping compilation
Multimeter.cpp
\Multimeter.h(25) : error C2504: 'CWinApp' : base class undefined
Multimeter.h(41) : error C2143: syntax error : missing ';' before '}'
Multimeter.h(41) : warning C4183: 'DECLARE_MESSAGE_MAP': missing return type; assumed to be a member function returning 'int'
MultimeterDlg.h(17) : error C2504: 'CDialog' : base class undefined
MultimeterDlg.h(20) : error C2143: syntax error : missing ')' before '*'
MultimeterDlg.h(20) : error C2143: syntax error : missing ';' before '*'
MultimeterDlg.h(20) : error C2460: 'CMultimeterDlg::CWnd' : uses 'CMultimeterDlg', which is being defined
MultimeterDlg.h(16) : see declaration of 'CMultimeterDlg'
MultimeterDlg.h(20) : error C2059: syntax error : ')'
MultimeterDlg.h(20) : error C2864: 'pParent' : only const static integral data members can be initialized inside a class or struct
MultimeterDlg.h(20) : error C2501: 'CMultimeterDlg::pParent' : missing storage-class or type specifiers
MultimeterDlg.h(25) : error C2146: syntax error : missing ';' before identifier 'm_currentValueEdit'
MultimeterDlg.h(25) : error C2501: 'CMultimeterDlg::CEdit' : missing storage-class or type specifiers
MultimeterDlg.h(25) : error C2501: 'CMultimeterDlg::m_currentValueEdit' : missing storage-class or type specifiers
MultimeterDlg.h(31) : error C2061: syntax error : identifier 'CDataExchange'
MultimeterDlg.h(45) : error C2144: syntax error : 'void' should be preceded by ';'
MultimeterDlg.h(45) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(46) : error C2144: syntax error : 'void' should be preceded by ';'
MultimeterDlg.h(46) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(47) : error C2146: syntax error : missing ';' before identifier 'HCURSOR'
MultimeterDlg.h(47) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(48) : error C2144: syntax error : 'void' should be preceded by ';'
MultimeterDlg.h(48) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(49) : error C2144: syntax error : 'void' should be preceded by ';'
MultimeterDlg.h(49) : error C2501: 'CMultimeterDlg::afx_msg' : missing storage-class or type specifiers
MultimeterDlg.h(52) : error C2143: syntax error : missing ';' before '}'
MultimeterDlg.h(52) : warning C4183: 'DECLARE_MESSAGE_MAP': missing return type; assumed to be a member function returning 'int'
Multimeter.cpp(17) : error C2061: syntax error : identifier 'CWinApp'
Multimeter.cpp(20) : error C2146: syntax error : missing ';' before identifier 'ON_COMMAND'
Multimeter.cpp(20) : error C2065: 'ID_HELP' : undeclared identifier
Multimeter.cpp(20) : error C2653: 'CWinApp' : is not a class or namespace name
Multimeter.cpp(20) : error C2065: 'OnHelp' : undeclared identifier
Multimeter.cpp(21) : error C2501: 'ON_COMMAND' : missing storage-class or type specifiers
Multimeter.cpp(21) : error C2078: too many initializers
Multimeter.cpp(21) : error C2146: syntax error : missing ';' before identifier 'END_MESSAGE_MAP'
Multimeter.cpp(26) : error C2143: syntax error : missing ';' before 'CMultimeterApp::__ctor'
Multimeter.cpp(26) : fatal error C1903: unable to recover from previous er
Yes, if you are porting to a managed app, you should remove the 'multimeter.h', 'multimeter.cpp', 'multimeterdlg.h' and 'multimeterdlg.cpp' files from the project.  They are no longer needed.  They get replaced with the Windows Forms version which is a '_tWinMain' function that starts an instance of the main form.
When I remove those files I get the following errors:



process22812read.cpp(27): error C3861: 'sprintf': identifier not found, even with argument-dependent lookup
process22812read.cpp(30): error C3861: 'sprintf': identifier not found, even with argument-dependent lookup
process22812read.cpp(34): error C3861: 'AfxMessageBox': identifier not found, even with argument-dependent lookup



connected with the following if:

if (i == SERIAL_READ_PKT_SIZE)
      {
            int msgptr = 0;
            char msg[256];
            msgptr += sprintf(msg, "22-812 checksum failure");
            for (int ctr = 0; ctr < SERIAL_READ_BUF_SIZE; ctr++)
            {
                  msgptr += sprintf(msg + msgptr, "% 02x", bBuf[ctr]);
            }
            //log(plogfile, msg);

            AfxMessageBox( msg );

            return 0.0;
      }
      else
      {
            double meter = Translate22812(&bBuf[i]);
            return meter;
      }

I wonder if this is not a part of the reading process from the multimeter and removing them (aside from the AfxMessageBox, maybe) will hurt the purpode of the program?  
In a managed app, sprintf should be replaced by "System::Console::Write" or "System::Console::WriteLine".  AfxMessageBox is an MFC construct and shoud be replaced by "System::Windows::Forms::MessageBox::Show".
Check that - Console::Write is a replacement for printf.  You want "System::String::Format"
Here's the new version of the code:

if (i == SERIAL_READ_PKT_SIZE)
     {
          System::Text::StringBuilder *msg = new System::Text::StringBuilder(256);
          msg->Append(S"22-812 checksum failure");
          for (int ctr = 0; ctr < SERIAL_READ_BUF_SIZE; ctr++)
          {
               msg->AppendFormat(S"{0:x02}", __box(bBuf[ctr]));
          }
          System::Windows::Forms::MessageBox::Show(msg->ToString());

          return 0.0;
     }
     else
     {
          double meter = Translate22812(&bBuf[i]);
          return meter;
     }
Still gives me these two error messages:

process22812read.cpp(36): error C2039: 'MessageBoxA' : is not a member of 'System::Windows::Forms'
process22812read.cpp(36): error C3861: 'Show': identifier not found, even with argument-dependent lookup

connected with:

System::Windows::Forms::MessageBox::Show(msg->ToString());
I remmed out the line:

System::Windows::Forms::MessageBox::Show(msg->ToString());

since it doesn't seem to contribute to the process of acquiring the data and the project compiles well. Now, how do I get the data obtained from the multimeter into the forms?
The #include od windows.h is messing you up.  MessageBoxA is the Win32 version of MessageBox.  After the #include's in the cpp file with this code, do:

#undef MessageBox

That will fix it.
Thanks. That did fix it. I still don't understand, however, how to get the data obtained from the multimeter into the forms, say, into Form1.h or into any other .h form.
Avatar of mrdtn

ASKER

drichards,

I don't know if this is completely resolved, but it seems as though it is well on it's way.  Based on your input here, please post a message to

https://www.experts-exchange.com/questions/21087424/Can-someone-help-finish-this-simple-port-This-is-easy-but-I-am-not-familiar-with-NET.html

so you can receive your points.

Judico, sorry I couldn't be of more help for the .NET stuff.  Nevertheless, isn't it cool how some problems get solved here?

mrdtn
mrdtn,

It's really great indeed. Thanks again for your help. I will continue, however, since I still don't know how to get the data from the multimeter into the forms.

judico
What you want to do is create a class (call it "MultiMeter" for the moment) and move all the serial port logic into it - give it methods "ConfigureSerialInterface", "Translate22812",  and "Process22812Read".  I'd also make the data buffer and COM port handle class members rather than passing them around as parameters.  Then create an instance of this class in Form1.  Also create a timer in Form1.  Use the same logic as before.  After the timer processing you can load the data into the form.  If you've got somewhere I can drop some code, I can do a first cut for you.  It's a bit much to post here.

Unfortunately I cannot test it for you since I don't have a device.
I wonder if you could post the code you mention on

http://nomorepasting.com/
Another possibility is for me to post the code I have in http://nomorepasting.com/ so that you can see in concrete terms what I have now. I will try to post all the .cpp and .h files I have and will give the link here.
It's a 12K zip file - I can mail it to you if you've got an address or I'll post it to my personal web site when I get home later.  The code is functional as near as I can tell - you just need to hook up the data to the controls.
Avatar of mrdtn

ASKER

Don't put email addresses in posts.  It's against EE policy -- and your inbox could get very full.  Instead you are allowed to put email in your profile info.

mrdtn
Avatar of mrdtn

ASKER

You can send it to me and I can put it where I put the other code.
Here is the code:

http://www.nomorepasting.com/paste.php?pasteID=18301

Hope it's legible. I'd like to have your code too. Will check later to see if you have posted in on your personal web site (I don't have the URL, however). Thanks.

judico
You can get the converted project here:

http://home.comcast.net/~n_a/WinMon.zip

It SHOULD get the reading and put it in the text box.
You'll notice that function_templates.h is also gone from my project.  All the functions were collected into the MultiMeter class.
Avatar of mrdtn

ASKER

drichards,

I have no means of trying the code you posted at this time, but it looks great and represents the transformation of what I had done into a class -- something I have intended to do at some point -- and then some.  Your contribution in my opinion deserves more points than I orignally specified on the .NET TA question (see link in above post), so I am upping that question to the maximum number.

Thanks for your contribution and for helping me help judico.

mrdtn
Avatar of mrdtn

ASKER

BTW,

All the reference to power supply stuff is non-applicable and get be gotten rid of if you choose.  My original app was monitoring more than just a thermocouple (via the 22-812).

mrdtn
One thing to note if you run my code - I have the timer running at 1 second intervals.  You'll probably want to change that.
drichards,

This is great. I would like to give you 500 points right away for what you just did. I will give you many more points as some of the details, still outstanding, are resolved.

Now, I'd like to ask you several things. First, the data begins to show in textBox1 only afted depressing the Close button. Why is that? Second, the data seen in textBox1 doesn't correpond to the readings I see on the display of the multimeter. At this moment I'm measuring resistance but the data displayed on the computer monitor shows something else. How can this be corrected?

P.S. In a moment I will post a special question entitled "Points for drichards" so that you can get your 500 points. Thanks again for the great help.
Yes, I saw what the meaning of this Close button is. I fixed it. That wasn't a substantial problem. The values that appear in the textBox1, however, have to reflect what really is being measured. I know, you don't have the multimeter to try the code. If needed I can send the multimeter to you, if you want, if we cannot resolve this issue here. Probably mrdtn can also help in this because originally it is his code.
I used the button labeled "Close" as the start button to begin serial port operations.  You evidently figured that out already.  You can put the startup code somewhere else or add a real start button (or a real close button).

I noticed some issues with the code that tries to interpret the meter readings.  I'll make some comments on it and post them here.  It looked a little odd.
Looking at the translation code, it looks correct - I checked against the protocol doc at the links provided by Moncapitaan.  The suspicious part is the initial processing code.  This code in 'Process22812Read':

      if (dwRead < 1) return 0.0;
      unsigned int i;

      for (i = 0; i < SERIAL_READ_PKT_SIZE; i++)


should probably look like this:

      int loopCount = dwRead + 1 - SERIAL_READ_PKT_SIZE;
      if (loopCount < 1) return 0.0;

      for (unsigned i = 0; i < loopCount; i++)

and then check:       if (i == loopCount)

to determine if you got good data.  The code as it stands could look past the end of actual data.  All you need to do to figure out what's amiss is set a break in the 'Process22812Read' routine and step through the processing.  You should see where it goes wrong.  If you want you can post a data buffer and the computed value.  I should be able to tell you what's going on from that.
And the checksum seems strange.  The code uses 0x39 as the constant while the doc says 0x57.  Interestingly, 0x39 = dec 57.  Makes one wonder.  Anyhow, the offer stands onthe data analysis if you can capture some data out of the debugger.
Avatar of mrdtn

ASKER

Hello all,

Just checking my inbox and it seems I may be able to shed some light on the past couple of comments.

First of all, as I think I stated in a WAY earlier post (this question has now grown considerably and I would certainly understand if that detail was overlooked), from my analysis, the checksum value specified in the links indicated hex 57 when it really meant decimal -- hence the correct observation by drichards.

As far as the code mentioned, the reasoning behind the logic is that each time the buffer is read, it reads enough to accommodate two screenfuls -- actually over-sampling the serial data stream.  The reason for this is to ensure enough of a data capture each time to be able to find a self-contained screen capture within.  Since capturing every consequtive screen update was not necessary for my application, over-samping so as to lose 50% of the samples was acceptable.  One could maintain a serial stream buffer through additional software if such is not acceptable.

Actually, what the code referenced above is doing is sifting through a double-wide data sample to find a "sync-boundary".  Once (and if) found, the data is interpreted.  For simplicity, I returned 0.0 under abnormal circumstances.  My original application was time-critical and I had to simplify things in some areas.  I need to study this further, but my initial reaction to the possibility that it "could look past the end of actual data" may not be based on the understanding that I am reading two screen-fulls-worth-of-data at a time.

mrdtn

--

judico,

Without a huge data dump, let me ask you to provide a few readings.  You say you were measuring impedance:

What does the meter read when the leads are shorted together? ___________________
What does the program show when the leads are shorted together? ___________________

If you have a known resistance you can measure, what does the meter read? ______________
With the same resistance reference, what does the program show? ______________

Does the program respond immediately to opening and short-circuiting the leads of the meter -- or is there a time lag of more than a second?  ___________

--

mrdtn

I made some minor changes and now the values come out right. I changed the numerical values in

aTimer = new System::Timers::Timer(1000);

and

comTimeOut.ReadTotalTimeoutConstant = 2500;

to 500 and 10, respectively. Also, changing the value 0x39 in

#define CHECKSUM_22812 0x39

to dec 57 doesn’t seem to make a difference. However, the replacement code which drichards suggested

int loopCount = dwRead + 1 - SERIAL_READ_PKT_SIZE;
if (loopCount < 1) return 0.0;
for (unsigned i = 0; i < loopCount; i++)
if (i == loopCount)

gives incorrect results.

. . . . . . . . . .

mrdtn,

These are the answers to your questions, after the minor corrections I made:

1) What does the meter read when the leads are shorted together?
Answer:  000.0

2) What does the program show when the leads are shorted together?
Answer: 0

3) If you have a known resistance you can measure, what does the meter read?
Answer: 9.92 (kOhm)

4) With the same resistance reference, what does the program show?
Answer: 9.92

5) Does the program respond immediately to opening and short-circuiting the leads of the meter -- or is there a time lag of more than a second?
Answer: The multimeter itself exhibits a slight time lag itself. The response of the program, however, is practically with no time lag to whatever the multimeter shows.

Thus, it seems the program is working properly now.

I have another question beyond the acquisition of the values – is it possible to set up the proper values of the baud rate, data bits, parity, stop bits and flow control from within the program and not go through start->Control Panel->System->Hardware->Device Manager (so that those values can correspond to the respective values in MultiMeter.cpp)?



P.S. I want to give points to both of you. Do you want me to do it through asking a question “Points for drichards and mrdtn”?
>> is it possible to set up the proper values of the baud rate, data bits, parity, stop bits and flow control from within
>> the program and not go through start->Control Panel->System->Hardware->Device Manager (so that those values
>> can correspond to the respective values in MultiMeter.cpp)?

Not sure what you mean.  The 'ConfigureSerialInterface' method sets up all those parameters except the flow control already.  There are other items in the DCB structure to set flow control if you need to change it.  These setting override whatever is set in the control panel.  Control panel settings are just defaults.  Does 'ConfigureSerialInterface' seem not to be working?
I see. Thanks.

I would be willing to try your code as well, if possible. Especially, if it works in a case when one needs to send a character to the device in order for the device to respond. In the case discussed here the device seems to be sending the data automatically to the port.



P.S. Please go to https://www.experts-exchange.com/questions/21089142/Points-for-drichards.html. I think you need to respond in order to get your points.
drichards and mrdtn,

Please go to https://www.experts-exchange.com/questions/21090196/Points-for-drichards-and-mrdtn.html where I have given you 500 points, 250 points to each of you. Thanks very much for the help.

judico
drichards and mrdtn,

Just to let you know -- I'm posting as a separate question the following: Is there a way to change from within the program the COM port to a predetermined port, say COM1?

judico