Re: EKM-Omnimeter RS-485 Data
Posted: Mon May 14, 2012 1:15 pm
by Jameson
Hi Tim,
This sounds really good! We are looking forward to seeing what you come up with. Labview looks like an interesting tool to use for displaying meter data. We are always interested in new ways to read the meters and are happy to hear you are willing to share the software when you are done. Thanks!
Meter Request:
The meter request is fairly simple. Here is the sample from the pdf: 2f 3f 30 30 30 30 30 30 30 31 30 30 31 35 21 0d 0a This is for meter # 10015
Here is an additional example for meter # 12345: 2f 3f 30 30 30 30 30 30 30 31 32 33 34 35 21 0d 0a
The rest of the data request does not change (there is no checksum for the request). Remember that the meter serial settings for the Omnimeter are 9600 baud, 7 data bits, even parity, 1 stop bit, and no flow control.
----
Return Data: Yes the return data length is always the same (255 bytes).
----
CRC Calculation (this is a bit wordy, so if you have any questions, please let us know): The CRC is a CRC-16 checksum. It is good if you can use this to determine if the data is good (but not essential) It gets a little challenging because our meters communicate with 7 bits rather than 8. The last 2 bytes in the response string are the CRC-16.
In this example string from an OmniMeter:
2 10 17 13 30 30 30 30 30 30 30 30 30 31 32 33 30 30 30 30 35 33 35 38 30 30 30 30 33 31 39 36 30 30 30 30 32 31 36 32 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 35 30 30 30 30 30 30 30 35 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 32 31 39 31 32 32 38 31 32 31 38 30 30 33 35 30 30 30 30 35 30 30 30 33 35 30 30 30 30 34 31 35 30 30 30 30 30 38 30 30 30 30 30 34 31 35 30 30 30 30 39 31 30 30 20 31 30 30 4C 30 39 38 43 30 39 31 30 30 34 37 32 35 30 30 31 31 30 30 38 32 36 30 35 30 37 31 37 33 38 35 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 21 D A 3 2B 6E
The checksum is the last 2 bytes, and is calculated on the second through the third from last bytes (H10 through H03, including the H10 and the H03). Because it is CRC-16, and because we use 7-bit bytes, we only have 16K possibilities with 2 bytes, as opposed to 64K possibilities using 8-bit bytes.
Here's one explanation:
1. An Initial 16-Bit CRC seems to have been calculated with the Polynomial 0xA001 or 0x5001. The routine for the CRC calculation is very standard. The way I figured these out is by a test program trying every polynomial between 0x0000 and 0xFFFF.
2. Then, depending on whether your processor is Big Endian, or Little Endian, the Bytes in the CRC16 need to be swapped (In my case, since Little Endian, the swap was required.)
3. Finally, Convert this 16-Bit CRC to a 14-Bit CRC by performing the following (and the following only) masking :
CRC &= 0x7F7F; -------> Thus, masking out the 7th, and 15th bits.
*******
Here is our CRC code... This is a standard lookup table implementation with the standard CRC16 polynomial, the differences in computation are in the code, dealing with the initial value, endianness, and 7-bit encoding.
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;
}