crc16 code to c#

For more advanced meter data users. Learn from others and ask questions about how to read meters, how to use the EKM Push Data, and how to display the meter data in useful ways.
Post Reply
CubeX
Posts: 8
Joined: Fri Nov 22, 2013 12:30 am

crc16 code to c#

Post by CubeX »

Good day everyone on the forum! I am writing a c# GUI app to read my EMK Omnimeter V4 and save the data to an output file for use with Aurora Communicator. I can read and de-parse return frames type A and B, but I am having difficulties with the crc conversion to c# :oops: . I was hoping someone can maybe assist. Here is the crc16 code as provided by Jameson from EKM:

Code: Select all

static const uint16_t const ekmCrcLut[256] = {
	0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
	0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
	0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
	0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
	0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
	0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
	0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
	0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
	0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
	0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
	0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
	0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
	0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
	0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
	0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
	0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
	0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
	0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
	0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
	0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
	0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
	0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
	0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
	0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
	0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
	0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
	0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
	0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
	0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
	0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
	0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
	0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 };

uint16_t ekmCheckCrc( const uint8_t const* dat, uint16_t len) 
{
	uint16_t crc = 0xffff;
	while (len--) 
	{
		crc = (crc >> 8) ^ ekmCrcLut[(crc ^ *dat++) & 0xff];
	}
	crc = (crc << 8) | (crc >> 8);
	crc &= 0x7f7f;
return crc;
}
I'd very much appreciate the assistance.
Jameson
Posts: 860
Joined: Fri Nov 04, 2011 7:42 pm
Location: Santa Cruz, CA
Contact:

Re: crc16 code to c#

Post by Jameson »

Sounds like a cool project! Im sure others with Power One solar inverters will be interested in following how this goes. Keep us posted!

I do not have an C# notes as such, but I do have notes for C and C++ (I have no idea if these will be helpful for you as I dont know how different these 3 Cs are)

Can you let us know more about what you are getting with your code and what you are expecting to get? If you do figure it out, would you be so kind as to post your solution and your results here?

Here is a customers Omnimeter CRC calc for C++
https://github.com/MojaveTom/QtReadEkm/ ... EkmCRC.cpp

And here is what I have for C in a file named ekm16crc.c It looks very similar to what you have, but is not exactly the same:

Code: Select all

#include <stdio.h>
#include <string.h>
#include <Python.h>


#define MAX_BUF 2048

static const unsigned short const ekmCrcLut[256] = {
	0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
	0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
	0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
	0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
	0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
	0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
	0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
	0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
	0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
	0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
	0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
	0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
	0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
	0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
	0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
	0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
	0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
	0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
	0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
	0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
	0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
	0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
	0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
	0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
	0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
	0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
	0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
	0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
	0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
	0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
	0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
	0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
};

unsigned short ekmCheckCrc(unsigned char dat[], unsigned short len) 
{
	unsigned short crc = 0xffff;

	while (len--) {
		crc = (crc >> 8) ^ ekmCrcLut[(crc ^ *dat++) & 0xff]; 
	}

	crc = (crc << 8) | (crc >> 8);
	crc &= 0x7f7f;

	return crc;
}


static PyObject* ekmcrc(PyObject* self, PyObject* args)
{
    const char* data = NULL;
    
    if (!PyArg_ParseTuple(args, "s", &data)){
        return NULL;
    }

    unsigned short crc_return = ekmCheckCrc((unsigned char *)data, strlen(data)); 

    char buf[64];
    sprintf(buf, "%x", crc_return);
    PyObject * ret = Py_BuildValue("s", buf);

    return ret;
}

static PyMethodDef EkmCrcMethods[] =
{
     {"ekmcrc", ekmcrc, METH_VARARGS, "EKM Meter CRC."},
     {NULL, NULL, 0, NULL}
};
 

PyMODINIT_FUNC initekmcrc(void)
{
     (void) Py_InitModule("ekmcrc", EkmCrcMethods);
}
Jameson
EKM METERING
http://www.ekmmetering.com
831.425.7371
CubeX
Posts: 8
Joined: Fri Nov 22, 2013 12:30 am

Re: crc16 code to c#

Post by CubeX »

Good day Jameson. Thank you for the help. I can definitely share the solution, I know how frustrating it can be so if I can help someone else I would be happy to do so!

The program I am writing reads all meter data that is packed into the two different frames, "30 30" and "30 31". It will then display it on the gui for development purposes, however the gui is not needed. In fact I am planning to use a daemon with no gui. The results of net power and some other values will be used alongside that of the solar inverters. I have 2 Aurora Uno 2.5 inverters with two 3kW arrays. The Inverters are read using Aurora Communicator. It creates a text file and uploads to PVOutput every 5 minutes. So my program will upload it's values directly from the meter to Aurora Communicator's CSV files where it will be uploaded to PVOutput by the PVOutput integration app.

The reason for all of this is because I can not upload directly to PVOutput, and that Wattvision have some errors unfortunately. My time stamping is out by 8 hours. On their website, my data is 8 hours behind but on PVOutput it is correct despite correctly configured time zones. The worst problem I have is that all power is seen as consumption coming from Wattvision. So my stats is incorrect. I am a paid user on PVOutput so my setup reflects nett usage from Wattvision, despite this I still have this problem.

Hope this sheds a little light on what my goals are here. I am learning lots of c# and the Omnimeter, all in all its a great little project for me.
CubeX
Posts: 8
Joined: Fri Nov 22, 2013 12:30 am

Re: crc16 code to c#

Post by CubeX »

Hi Jameson. I managed to convert the code to c#, but I can not seem to get the correct result for some reason. I need to verify crc for received data and sent data. Is there a difference?

Here is the c# code:

Code: Select all

        public static readonly ushort[] ekmCrcLut = {
        0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
        0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
        0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
        0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
        0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
        0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
        0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
        0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
        0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
        0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
        0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
        0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
        0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
        0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
        0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
        0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
        0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
        0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
        0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
        0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
        0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
        0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
        0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
        0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
        0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
        0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
        0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
        0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
        0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
        0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
        0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
        0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 };

        ushort ekmCheckCrc(byte[] dat)
        {
            uint crc = 0xffff;
            for (int i = 0; i < dat.Length; i++)
            {
                crc = (crc >> 8) ^ ekmCrcLut[(crc ^ dat[i]) & 0xff];
            }

            crc = (crc << 8) | (crc >> 8);
            crc &= 0x7f7f;
            return (ushort)crc;
        }
As an example for sent data, here is the check password string sent to the meter by EKMDash for an actual Omnimeter Pulse v4:
01 50 31 02 28 30 30 30 30 30 30 30 30 29 03 32 44
Break down if I am right:
01 50 31 02 28
Start frame 01, 50 31 02 28 ???
30 30 30 30 30 30 30 30
Password 00000000
29 03
not sure?
32 44
crc?

What it actually used for the crc calculation, is it every second character apart from "32 44", so crc check string is then "101280000000093"?
Jameson
Posts: 860
Joined: Fri Nov 04, 2011 7:42 pm
Location: Santa Cruz, CA
Contact:

Re: crc16 code to c#

Post by Jameson »

CRC is calculated from the second character to the last character (not including the CRC bytes)

For Password check it would be calculated on this string:

50 31 02 28 30 30 30 30 30 30 30 30 29 03

Here is a document that shows this: http://documents.ekmmetering.com/EKM-Om ... ol_v.3.pdf

Hope this helps,
Jameson
EKM METERING
http://www.ekmmetering.com
831.425.7371
Post Reply