NAV
Command Line Ruby Python PHP Perl Java C NodeJS Javascript

Introduction

Welcome to the EKM METERING API!

We at EKM see easy access to your data, and the scalable systems behind the EKM Push, as crucial to moving our products into the future. To that end, we do what is unheard of in our industry, we give you your data for FREE.

The EKM API is organized around Representational State Transfer, or REST. You can use our Application Programming Interface, or API, to access EKM API endpoints, which can get you information on various EKM Push meter data and utilize it in your own application, database, billing system, or building automation system.

We have language bindings in Shell (cURL), Ruby, Python, PHP, Java, Javascript, Nodejs and C! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

Our API is designed to have predictable, resource-oriented URLs and to use HTTP response codes to indicate API errors. We use built-in HTTP features, like HTTP authentication and HTTP verbs, which can be understood by off-the-shelf HTTP clients, and we support cross-origin resource sharing to allow you to interact securely with our API from a client-side web application (though you should remember that you should never expose your secret EKM Push API key in any public website’s client-side code). JSON will be returned in all responses from the API, including errors (though if you’re using API bindings, we will convert the response to the appropriate language-specific object).

Authentication

To authorize, make sure to use your personal EKM Push account key.

The examples in this API documentation use the demo key of MTAxMDoyMDIw. Please make sure you remove this key and place your personal key in the https address if you are trying to access the meters in your account.

With shell, you can just pass the correct address with each request
curl -s "URL Here"
Authorization: "EKM Push Key"
# Load required modules
require 'net/http'
require 'json'

# Call the callApi method to create a usable
# object named apiObject from the API request URI
# Put the API request URI in the call
def callApi(apiRequest)
    json = Net::HTTP.get('io.ekmpush.com',apiRequest)
    return JSON.parse(json)
end

# Call the callApi method to create a usable
# object named apiObject from the API request URI
# Put the API request URI in the call
# URI only NOT URL - Do not include https://io.ekmpush.com
apiObject = callApi('URI Here')

# This just displays the object but you can use what ever
# code you would like to work with the object here
require 'pp'
pp apiObject
# Required Python Modules
import urllib.request, urllib.error, urllib.parse
import json

# This function accesses the apiRequest URL and converts
# the contents to a usable Python object and returns it
def callApi ( apiRequest ):
        response = urllib.request.urlopen(apiRequest)
        response = response.read()
        jsonObject = json.loads(response.decode())
        return jsonObject

# Call the callApi function to create a usable
# object named apiObject from the API request URL.
# Put the API request URL in the call
apiObject = callApi("URL Here")

# This just displays the object but you can use what ever
# code you would like to work with the object here
import pprint
pprint.pprint(apiObject)
<?php
// Call the callApi function to create a usable
// object named $apiObject from the API request URL.
// Put the API request URL in the call
$apiObject=callApi('URL Here');

// This just displays the object but you can use what ever
// code you would like to work with the object here
var_dump($apiObject);

// This function accesses the apiRequest URL and converts
// the contents to a usable PHP object and returns it
function callApi ($apiRequest='') {

        $json=@file_get_contents($apiRequest);
        $jsonObject=json_decode($json);
        return ($jsonObject);

}
?>
/*
 Download the correct org.json jar version for your
 needs from: https://mvnrepository.com/artifact/org.json/json

 This example uses version 20190722 accessible here:
 https://repo1.maven.org/maven2/org/json/json/20190722/json-20190722.jar

 Instructions to run this program

 1. Put this code in a file named EKM.java
 2. Copy the downloaded org.json jar and EKM.java to the same directory
 3. Compile
  javac -cp .:./json-20190722.jar ./EKM.java
 4. Run
  java -cp .:./json-20190722.jar EKM
*/


//Import required classes
import java.net.*;
import java.io.*;
import org.json.*;

public class EKM {
    public static JSONObject callApi(String apiRequest) throws Exception {

        // This code accesses the apiRequest URL and converts
        // the contents to a usable JSON object

        URL url = new URL(apiRequest);
        URLConnection connection = url.openConnection();
        BufferedReader in = new BufferedReader(
                                               new InputStreamReader(
                                                                     connection.getInputStream()));

        StringBuilder response = new StringBuilder();
        String inputLine;

        while ((inputLine = in.readLine()) != null)
            response.append(inputLine);

        in.close();

        JSONObject jsonObject = new JSONObject(response.toString());
        return jsonObject;
    }

    public static void main(String[] args) throws Exception {
        /*
         Call callApi to create a usable
         object named apiObject from the API request URL.
         Put the API request URL in the call
         */
        JSONObject apiObject = EKM.callApi("URL Here");

        /*
         You can access any part of the apiObject using code like this:
         JSONArray  readData = apiObject.getJSONObject("readMeter").getJSONArray("ReadSet").
         getJSONObject(0).getJSONArray("ReadData");
        */

        // This just outputs the whole apiObject
        System.out.println(apiObject.toString(4));
    }
}
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">

// The example function is called from the 
// body tag when the page loads
function example(){

// Call the callApi function to create a usable
// object named apiObject from the API request URL.
// Put the API request URL in the call
callApi('URL Here',function(apiObject){

       // This just displays the object in the result div
       // you can use what ever code you would like to work 
       // with the object here
       document.getElementById("result").innerHTML = "<pre>"+JSON.stringify(apiObject, null, 4)+"</pre>";
       });

};

// This code accesses the apiRequest URL and converts
// the contents to a usable JSON object named apiObject
function callApi(apiRequest,callback) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (xhttp.readyState == 4 && xhttp.status == 200) {
        var jsonObject = JSON.parse(xhttp.responseText);
        callback(jsonObject);
      }
    };
    xhttp.open("GET", apiRequest, true);
    xhttp.send();
}
</script>

</head>
  <body onload="example()">
    <div id="result"/>
  </body>
</html>
// NodeJS requires the "request" module to access the API
// Install by running "npm install request" from the shell prompt

// Load "request" module
var request = require("request");

// Call the callApi function to create a usable
// object named apiObject from the API request URL.
// Put the API request URL in the call
callApi('URL Here', function (apiObject) {


        // This just displays the object but you can use what ever
        // code you would like to work with the object here
        console.log(JSON.stringify(apiObject, null, 4));
});

// This code accesses the apiRequest URL and converts
// the contents to a usable JSON object named apiObject
function callApi(apiRequest,callback){

        var options = { method: 'GET'
                        , uri: apiRequest
                        , timeout: 1000*15
                        , strictSSL: false
                        , rejectUnhauthorized : false
                      };

        request(options, function (error, response, body) {
                if(error){
                        console.log(error);
                        throw error;
                }else{
                     var jsonObject = JSON.parse(body);
                     callback(jsonObject);
                }
        });
}
/*
 *  Install json-c if its not installed
 *  https://github.com/json-c/json-c/wiki
 *
 *  Below is command to install json-c on centos
 *  yum install json-c-devel
 *
 *  Saved this example code to a file named ekm.c
 *  - Compile
 *  gcc ekm.c -o ekm -lcurl  -ljson-c
 *  - Run
 *  ./ekm
 *
 */
// Required Includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h> //your directory may vary depending on your OS
#include <json/json.h> //your directory may vary
// End Includes

// Code reqired for curl
struct string {
    char *data;
    size_t len;
};

void init_string(struct string *response) {
    response->len = 0;
    response->data = (char*)malloc(response->len+1);
    if (response->data == NULL) {
        fprintf(stderr, "malloc() failed\n");
        exit(EXIT_FAILURE);
    }
    response->data[0] = '\0';
}

size_t curlData(void *data, size_t size, size_t nmemb, struct string *response)
{
    size_t new_len = response->len + size*nmemb;
    response->data = (char*)realloc(response->data, new_len+1);
    if (response->data == NULL) {
        fprintf(stderr, "realloc() failed\n");
        exit(EXIT_FAILURE);
    }
    memcpy(response->data+response->len, data, size*nmemb);
    response->data[new_len] = '\0';
    response->len = new_len;

    return size*nmemb;
}
// End required Curl coding

// This function accesses the api URL and converts
// the contents to a usable JSON-C object and returns it
struct json_object* callAPI(const char * url){
    CURL *curl;
    CURLcode res;
    json_object * jsonObject;
    curl = curl_easy_init();
    if(curl) {
        struct string response;
        init_string(&response);

        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlData);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        res = curl_easy_perform(curl);
        if(CURLE_OK == res){
            jsonObject = json_tokener_parse(response.data);
        }
        free(response.data);
    }

    curl_easy_cleanup(curl);
    curl_global_cleanup();
    return jsonObject;
}

int main(void)
{

    // Call the callApi function to create a usable
    // object named apiObject from the API request URL.
    // Put the API request URL in the call
    json_object * apiObject = callAPI("URL Here");

    // This just displays the object but you can use what ever
    // code you would like to work with the object here
    json_object * readmeter_json;
    json_object_object_get_ex(apiObject,"readMeter",&readmeter_json);
    printf("Complete readMeter Object: \n%s", json_object_to_json_string_ext(readmeter_json,JSON_C_TO_STRING_PRETTY));

    // This example digs deaper into the JSON and displays the first
    // kwh_tot value for the first read of the first meter
    json_object * readset_json;
    json_object * readset_0_json;
    json_object * readdata_json;
    json_object * readdata_0_json;
    json_object * kwh_tot;
    json_object_object_get_ex(readmeter_json,"ReadSet",&readset_json);
    readset_0_json = json_object_array_get_idx(readset_json,0);
    json_object_object_get_ex(readset_0_json,"ReadData",&readdata_json);
    readdata_0_json = json_object_array_get_idx(readdata_json,0);
    json_object_object_get_ex(readdata_0_json,"kWh_Tot",&kwh_tot);
    printf("\nkWh_Tot: %s", json_object_to_json_string_ext(kwh_tot,JSON_C_TO_STRING_PRETTY));       

    return 0;
}

Make sure to replace the sample key: MTAxMDoyMDIw, with your API key in the https address.

EKM uses API keys to allow access to the API. You authenticate to the EKM API by providing one of your unique API keys in each request. Each Push account holder is provided with an EKM Push User Key, which provides access to all meters in their account. This key carries lots of privileges so we encourage you to keep it secret. In addition to this master key, additional keys are also provided to give access to each meter individually, and keys can be created to provide access to sub groups of meters upon request. These secondary keys can be used to share single meters, or a subset of meters, without sharing access to all meters in an account. For example, if you are a landlord with multiple rentals and meters, you could share specific meter keys with each of your tenants, so that they could have access to only the data that pertains to their usage.

Authentication to the API occurs via HTTP Basic Auth. Provide your API key as the basic authorized username. You do not need to provide a password. You must authenticate for all requests.

The EKM Push API expects the API key to be included in all requests to the server. The key is included in the URL in the following way:

Authorization: key=MTAxMDoyMDIw

Realtime API

Click here to go to Realtime Documentation

If you are developing your own app, cloud-to-cloud solution, billing system, or other SAS solution, our Real-Time API allows you to easily access your EKM Push data in any format that you need. Below you will find descriptions regarding how to access the data, and about the filters you can apply so the data comes to you in a format that is easily digested and inserted into your software solution.

The real-time API provides the 1000 latest meter reads for each of your meters. If your meter is being read once per minute, the data will be made available once per minute, per meter. Whether you have 1 meter or 10,000 meters, this is the easiest and most scalable way to access your data.

The EKM Dash, EKM Widget, encompass.io, wattvision.com, pvoutput.org, the other solutions in our Push App Store, as well as other customers that have their own custom solutions, all use this API to access their data. We use the same API as you and do not give ourselves any special permissions, we see what you see, which forces us to make the API as great as possible for everyone. We have even given you code examples that can be copy and pasted into your own software language to make the data access that much easier.

Use the API definition, metered values definition, code snippet suggestion, and guide to get you on your way to developing your next killer app. If you create something great, let us know; we’re open to adding all useful apps into the Push App Store.

We also have a Realtime API Request Builder Tool found here: https://apibuilder.ekmmetering.com

We also have Documentation for our Legacy Realtime API here: Legacy Realtime API

Summary API

Click here to go to Summary Documentation

Our Summary API takes every Real-Time read, over 15 minute time periods, and summarizes them into single 15 minute summaries. We store this data forever to provide a long term historical dataset for each meter. Our system can then combine these summaries together to summarize hours, days, weeks, and months. This dataset is often the best way to get historical values like kWh, pulse counts, etc. It also provides averages, min. and max. values, difference, and more. We make this data available to you via our Summary API in a very similar way to our Real-Time API.

You can use the Summary API definition to access the data you need, from 15 minutes to years of data. We have gone to great lengths to provide this data for free in order to add value to our metering systems. The Summary API, the Real-Time API, great affordable hardware, and scalable access to your data are all components of the most powerful, and highest value metering system available in the world.

We also have a Summary API Request Builder Tool found here: https://apibuilder.ekmmetering.com/summary.html

RS-485 Communications

This section is for developers, or individuals, that want to communicate with their EKM Meters directly using their own software or embedded solution.

The code examples found in this section are in the simplest possible method to get you started. You are welcome to make them more robust.

First we start you out just connecting to the meter. You will find there is a very simple command line process to help you get started.

After that we cover the CRC method required for most communication to the meter.

Then we put it all together, in a simple test script that will show reads, and also open and close the relays.

Last we cover how to convert the 255 character strings that the meter responds with to a usable array containing field names and their values. It is our hope that after you go through these steps you will have all the information you need, to build whatever tools you like to access the meter.

Our meters use an IEC 62056-21 communication standard that has been optimized for our own needs. We are more than happy to share this with you. With this you can write your own software or embedded solution to access your meter data.

IEC 62056 is a set of standards for Electricity metering data exchange by International Electrotechnical Commission.

To learn more about the IEC 62056 standard click the button below to visit the WikiPedia website.

Additional PDF docs are available:

v.3 Meter Settings Protocol

v.3 Meter Parsing

v.4 Meter Settings Protocol

v.4 Meter Parsing

v.4 Meter Alternate Modbus Protocol

If you are coding in Python you can also make use of our ekmmeters.py API.

RS-485 Serial Settings

Send any of your meters on your RS-485 network this simple request (including the 12 digit meter number) and the targeted meter will respond with most of its data. If you use our EKM Dash software and utilize the “Hex Inspector” functionality under the “Help” menu item, it will simplify your job to be able to “see” the request and response (this is helpful for both the RS-485 parsing as well as parsing the EKM Push .xml data).

Serial Settings for v.3 and v.4 meters: 7 data bits, Even Parity, 1 Stop Bit, No Flow Control, 9600 baud rate.

Always send the “Close String” at the end of the transaction. This tells the meter that the communication session is over.

Close String Parameter

Parameter Description
01 42 30 03 75 Close String to end session

The last 2 bytes of the RS-485 return string are a CRC-16 checksum.

All values are Hexadecimal.

Example: 30 represents a 0, 31 represents a 1, 32 represents a 2, etc.

Hex to ASCII examples

Parameter Description
30 represents a 0
31 represents a 1
32 represents a 2
33 represents a 3
34 represents a 4
35 represents a 5
etc. This sequence continues

For a complete list of ASCII code and the Hex equivalent please click the button below.

Click Here

RS-485 Connecting to Meter

Connecting to Meter


# The serial device in this example is /dev/ttyUSB0

# First set up your serial port
stty -F /dev/ttyUSB0 cs7 -parodd -cstopb -ixon 9600

# Now we start hexdump to monitor the serial port
hexdump -v -e '1/1 "%02x "' /dev/ttyUSB0 &

# In this example we are just going to set a Request A call using echo
# The meter number is 000300001184 (in hex 30 30 30 33 30 30 30 30 31 31 38 34)
echo -en "\x2F\x3F\x30\x30\x30\x33\x30\x30\x30\x30\x31\x31\x38\x34\x30\x30\x21\x0D\x0A" > /dev/ttyUSB0

# Now we can kill hexdump to stop monitoring the serial port
killall -9 hexdump

# Requires the SerialPort gem
# (http://rubygems.org/gems/serialport)
# gem install serialport


require "serialport"

# Params for serial port
# Change /dev/ttyUSB0 to match your syste
port_str = "/dev/ttyUSB0"
baud_rate = 9600
data_bits = 7
stop_bits = 1
parity = SerialPort::EVEN
flow = SerialPort::NONE

# Meter Number
meter="000300001184"

# Open connection to serial port
sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits, parity)
sp.flow_control = flow

# Send Request A to v4 Meter
sp.write("\x2F\x3F"+meter+"\x30\x30\x21\x0D\x0A")

# Set connection timeout
timeout=5

# Get meter response
now=Time.now
read=""
response=""
count=0
while count<255
  read=sp.getc

  # Checks for correct start of message from meter before storing reads
  if read=="\x02"
    start=1
  end

  if read && start
    now=Time.now
    count+=1
    response.concat(read)
  end

  if Time.now > now + timeout
    print("Connection timed out!")
    break
  end

end

# Show response as HEX
print(response.unpack('C*').map {|e| e.to_s 16}.join(" "))

# Send close to meter
sp.write("\x01\x42\x30\x03\x75")

# Close connection to serial port
sp.close
# Requires pyserial module
# pip3 install pyserial

import serial
import codecs

#simple helpers for dealing with hex/str conversion
def hex2str(string):
    return codecs.decode(codecs.decode(string,"hex"),"ascii")

def str2hex(string):
    return codecs.decode(codecs.encode(string.encode(),"hex"),"ascii")

# Open connection to serial port
# Change /dev/ttyUSB0 to match your system
sp = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=9600,
    parity=serial.PARITY_EVEN,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.SEVENBITS,
    xonxoff=0,
    timeout=5
)

# Meter Number
meter=b"000300001184"

# Send Request A to v4 Meter
sp.write(b"\x2F\x3F"+meter+b"\x30\x30\x21\x0D\x0A")

# Get meter response
response = sp.read(255)

# Show response
print(" ".join("{0:02x}".format( c ) for c in response))

# Send close to meter
sp.write(b"\x01\x42\x30\x03\x75")

# Close connection to serial port
sp.close
<?php
// Requires the pecl_dio extenion
// On CentOS
// yum install php-pear
// pecl install dio

// Open connection to serial port
// Change /dev/ttyUSB0 to match your system
$sp = dio_open( '/dev/ttyUSB0', O_RDWR | O_NONBLOCK );

// Configure the port
dio_tcsetattr( $sp, array(
   'baud'         => 9600,
   'bits'         => 7,
   'stop'         => 1,
   'parity'       => 2,
   'flow_control' => 0,
   'is_canonical' => 0
) );

// Meter Number
$meter="000300001184";

// Send Request A to v4 Meter
dio_write( $sp, "\x2F\x3F".$meter."\x30\x30\x21\x0D\x0A" );

# Set connection timeout
$timeout=10;

// Get meter response
$read = '';
$response = '';
$start=0;
$now=time();
while( strlen($response)<255 && time()<$now+$timeout) {
   $read = dio_read($sp,1);
   # Checks for correct start of message from meter before storing reads
   if ($read=="\x02")
    $start=1;

   if ($start){
     $response .= $read;
   }
}

// Show response as HEX
echo strToHex($response);

// Send close to meter
dio_write($sp, "\x01\x42\x30\x03\x75" );

// Close connection to serial port
dio_close( $sp );

function strToHex($string)
{
    $hex='';
    for ($i=0; $i < strlen($string); $i++)
    {
        $hex .= sprintf("%02X",ord($string[$i])).' ';
    }
    return $hex;
}

?>
# Requires the Device::SerialPort module
# On CentOS
# yum install perl-Device-SerialPort

use Device::SerialPort;

# Open connection to serial port
# Change /dev/ttyUSB0 to match your system
my $sp = new Device::SerialPort("/dev/ttyUSB0");

# Configure the port
$sp->user_msg(ON);
$sp->baudrate(9600);
$sp->parity("even");
$sp->databits(7);
$sp->stopbits(1);
$sp->handshake("xoff");
$sp->write_settings;


# Meter Number
my $meter="000300001184";

# Send Request A to v4 Meter
$sp->write("\x2F\x3F$meter\x30\x30\x21\x0D\x0A");

# Set connection timeout
my $timeout=10;

# don't wait for each character
$sp->read_char_time(0);     
# wait for 1 second per unfulfilled read
$sp->read_const_time(1000);

# Get meter response
my $chars=0;
my $response="";
while ($timeout>0 && $chars<255) {
       my ($count,$read)=$sp->read(255);
       if ($count > 0) {
               $chars+=$count;
               $response.=$read;
           }
       else {
             $timeout--;
       }
}

if ($timeout==0) {
    die "Connection timed out!\n";
}

# Show response as HEX
$response =~ s/(.)/sprintf("%02X",ord($1))." "/seg;
print $response;

# Send close to meter
$sp->write("\x01\x42\x30\x03\x75");

# Close connection to serial port
$sp->close || die "failed to close";
undef $sp;
/*
 Download the jssc.jar (java-simple-serial-connector)
 from: https://code.google.com/archive/p/java-simple-serial-connector/

 Instructions to run this program

 1. Put this code in a file named connect.java
 2. Copy the downloaded jssc.jar and connect.java to the same directory
 3. Compile
  javac -cp .:./jssc.jar ./connect.java
 4. Run
  java -cp .:./jssc.jar connect
*/

// Import required classes
import jssc.*;
import java.lang.*;
public class connect {

public static void main(String[] args) {
    String response = "";
    byte[] buffer = "\r\n".getBytes();
    SerialPort serialPort = new SerialPort("/dev/ttyUSB0");
    try {
    // Open connection to serial port
    // Change /dev/ttyUSB0 to match your system

        serialPort.openPort();
    // Configure the port
        serialPort.setParams(SerialPort.BAUDRATE_9600,
                             SerialPort.DATABITS_7,
                             SerialPort.STOPBITS_1,
                             SerialPort.PARITY_EVEN);
    serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);


    // Meter Number
        String  meter="000300001184";

    // Send Request A to v4 Meter
    String  messageString = "\u002F\u003F"+meter+"\u0030\u0030\u0021\r\n";
        serialPort.writeBytes(messageString.getBytes());

    // Set connection timeout;
    int timeout=5;

    // Get meter response
        try {
    buffer = serialPort.readBytes(255,timeout*1000);
    } catch (SerialPortTimeoutException e1)
    {
        System.out.println("Connection Timed out");
    }
        response = new String(buffer);

        // Show response as HEX
        System.out.println(byteArrayToHex(response));

        // Send close to meter
        serialPort.writeBytes("\u0001\u0042\u0030\u0003\u0075".getBytes());

        // Close connection to serial port
        serialPort.closePort();
    }
    catch (SerialPortException ex) {
        System.out.println(ex);
    }
}

public static String byteArrayToHex(String OrgStr) {
   byte[] a = OrgStr.getBytes();
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a){
      sb.append(String.format("%02x", b & 0xff));
      sb.append(" ");
   }
   return sb.toString();
}

}

/*
 *  Saved this example code to a file named connect.c
 *  - Compile
 *  gcc connect.c -o connect
 *  - Run
 *  ./connect
 */
// Required Includes

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>   /* File Control Definitions */
#include <termios.h> /* POSIX Terminal Control Definitions */
#include <unistd.h>  /* UNIX Standard Definitions */
#include <errno.h>   /* ERROR Number Definitions */


void main(void){
        // Open connection to serial port
        // Change /dev/ttyUSB0 to match your system
        int sp = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY );

        // Error Checking
        if(sp == -1){
               perror("Error! in Opening /dev/ttyUSB0");
               exit(-1);
        }

        // Setting the Attributes of the serial port using termios structure
        struct termios SerialPortSettings;

        //Get the current attributes of the Serial port
        tcgetattr(sp, &SerialPortSettings);

        //Setting the Baud rate
        cfsetispeed(&SerialPortSettings,B9600); /* Set Read  Speed as 9600 */
        cfsetospeed(&SerialPortSettings,B9600); /* Set Write Speed as 9600 */

        SerialPortSettings.c_cflag |=  PARENB;
        SerialPortSettings.c_cflag &= ~CSTOPB;
        SerialPortSettings.c_cflag &= ~CSIZE;
        SerialPortSettings.c_cflag |=  CS7;
        SerialPortSettings.c_cflag &= ~CRTSCTS;
        SerialPortSettings.c_iflag |= IXOFF;
        SerialPortSettings.c_iflag &= ~ICANON;

        // Read buffer size
        SerialPortSettings.c_cc[VMIN] = 255;

            // Meter Number
        char *meter="000300001184";

        // Send Request A to v4 Meter
                char write_buffer[19];
                sprintf(write_buffer, "\x2F\x3F%s\x30\x30\x21\x0D\x0A", meter);
                write(sp,write_buffer,sizeof(write_buffer));

        // Set connection timeout
        int timeout = 5;
        SerialPortSettings.c_cc[VTIME] = timeout*10;

        // apply new serial port settings
        tcsetattr(sp,TCSANOW,&SerialPortSettings);
        // discards old data in the buffer
        tcflush(sp, TCIFLUSH);   

        // Initialize file descriptor sets
        fd_set read_fds, write_fds, except_fds;
        FD_ZERO(&read_fds);
        FD_ZERO(&write_fds);
        FD_ZERO(&except_fds);
        FD_SET(sp, &read_fds);

        struct timeval timeoutStruct;
        timeoutStruct.tv_sec = timeout;
        timeoutStruct.tv_usec = 0;

            // Get meter response
        if (select(sp + 1, &read_fds, &write_fds, &except_fds, &timeoutStruct) == 1){
                char response[255];
                    read(sp,&response,255);

        // Show response as HEX
        const char *ptr = response;
        while(*ptr)
                                        printf("%02X ", (unsigned int) *ptr++);
                }

        // Send close to meter
        char close_buffer[] = "\x01\x42\x30\x03\x75";

        // Close connection to serial port
        write(sp,close_buffer,sizeof(close_buffer));

        close(sp);
}

// NodeJS requires the "request" module to connect with meter
// Install by running "npm install serialport" from the shell prompt

// Load "serialport" module
var serialport = require("serialport");
var SerialPort = serialport.SerialPort;

var sp = new SerialPort("/dev/ttyUSB0", {
        baudRate: 9600,
        parity: 'even',
        dataBits: 7,
        stopBits: 1,
        flowControl: 0,
        bufferSize: 1,
        parser: serialport.parsers.raw
}, false);


// Meter Number
var meter="000300001184";

//Set connection timeout
var timeout = 5;
var response = new Buffer(0);
var start = 0;

function write() {
    sp.open(function(err) {
        start = 0;
        response = new Buffer(0);

        sp.on('error', function(err){
            console.log("Error"+err);
        });
        // Get meter response
        sp.on('data', function(read) {
            if(response.length<255){
                if(read.toString('hex')== "02"){
                    start=1;
                }
                if(start){
                    response = Buffer.concat([response, read]);
                }
            }
        });

        // Send Request A to v4 Meter
        sp.write("\x2F\x3F"+meter+"\x30\x30\x21\x0D\x0A", function(err, res) {
            if (err) { console.log(err);}
            //wait till timeout
            setTimeout(showResponse, timeout*1000);
        });

    });
}

function showResponse(){
    if(response.length==255){
        //Show response as HEX
        console.log(strToHex(response.toString()));
    }else{
        console.log("Connection timed out!");
    }
    // reset variables
    response = new Buffer(0);
    start = 0;

    // Send close to meter
    sp.write("\x01\x42\x30\x03\x75", function(err, res){
        if (err) { console.log(err); }
        // Close connection to serial port
        sp.close();
    });
}


function strToHex(str) {
    var hex = '';
    for(var i=0;i<str.length;i++) {
        var pad = ""+str.charCodeAt(i).toString(16).toUpperCase();
        hex += ' '+ ("00"+pad).slice(-2);
    }
    return hex;
}

//wait 1s for everything to initialize correctly
setTimeout(write, 1000);


For security reasons browsers have very limited access to the machines resources, writing browser ran Javascript (client-side) is not a viable option

The above example returns the following results:


02 10 24 15 30 30 30 33 30 30 30 30 31 31 38 34 30 30 30 30 30 30 32 36 30 30 30 30 30 30 31 30 30 30 30 30 30 30 32 36 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 36 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 31 32 31 31 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 43 30 39 39 43 30 30 30 43 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 36 30 30 33 30 30 31 30 33 31 33 34 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 31 30 30 30 31 37 30 35 32 35 30 35 30 31 30 39 35 38 30 30 21 0d 0a 03 6c 16


2 10 24 15 30 30 30 33 30 30 30 30 31 31 38 34 30 30 30 30 30 30 32 36 30 30 30 30 30 30 31 30 30 30 30 30 30 30 32 36 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 36 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 31 32 31 31 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 43 30 39 39 43 30 30 30 43 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 36 30 30 33 30 30 31 30 33 31 33 34 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 31 30 30 30 31 37 30 35 32 35 30 35 31 36 35 31 32 30 30 30 21 d a 3 34 57

02 10 24 15 30 30 30 33 30 30 30 30 31 31 38 34 30 30 30 30 30 30 32 36 30 30 30 30 30 30 31 30 30 30 30 30 30 30 32 36 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 36 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 31 32 31 31 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 43 30 39 39 43 30 30 30 43 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 36 30 30 33 30 30 31 30 33 31 33 34 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 31 30 30 30 31 37 30 35 32 35 30 35 30 31 30 39 35 38 30 30 21 0d 0a 03 6c 16

02 10 24 15 30 30 30 33 30 30 30 30 31 31 38 34 30 30 30 30 30 30 32 36 30 30 30 30 30 30 31 30 30 30 30 30 30 30 32 36 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 36 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 31 32 31 31 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 43 30 39 39 43 30 30 30 43 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 36 30 30 33 30 30 31 30 33 31 33 34 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 31 30 30 30 31 37 30 35 32 35 30 35 30 31 30 39 35 38 30 30 21 0d 0a 03 6c 16

02 10 24 15 30 30 30 33 30 30 30 30 31 31 38 34 30 30 30 30 30 30 32 36 30 30 30 30 30 30 31 30 30 30 30 30 30 30 32 36 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 36 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 31 32 31 31 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 43 30 39 39 43 30 30 30 43 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 36 30 30 33 30 30 31 30 33 31 33 34 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 31 30 30 30 31 37 30 35 32 35 30 35 30 31 30 39 35 38 30 30 21 0d 0a 03 6c 16
02 10 24 15 30 30 30 33 30 30 30 30 31 31 38 34 30 30 30 30 30 30 32 36 30 30 30 30 30 30 31 30 30 30 30 30 30 30 32 36 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 36 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 31 32 31 31 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 43 30 39 39 43 30 30 30 43 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 36 30 30 33 30 30 31 30 33 31 33 34 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 31 30 30 30 31 37 30 35 32 35 30 35 30 31 30 39 35 38 30 30 21 0d 0a 03 6c 16
02 10 24 15 30 30 30 33 30 30 30 30 31 31 38 34 30 30 30 30 30 30 32 36 30 30 30 30 30 30 31 30 30 30 30 30 30 30 32 36 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 36 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 31 32 31 31 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 43 30 39 39 43 30 30 30 43 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 36 30 30 33 30 30 31 30 33 31 33 34 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 31 30 30 30 31 37 30 35 32 35 30 35 30 31 30 39 35 38 30 30 21 0d 0a 03 6c 16
02 10 24 15 30 30 30 33 30 30 30 30 31 31 38 34 30 30 30 30 30 30 32 36 30 30 30 30 30 30 31 30 30 30 30 30 30 30 32 36 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 32 36 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 31 32 31 31 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 43 30 39 39 43 30 30 30 43 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 36 30 30 33 30 30 31 30 33 31 33 34 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 31 30 30 30 31 37 30 35 32 35 30 35 30 31 30 39 35 38 30 30 21 0d 0a 03 6c 16

Send any of your meters on your RS-485 network this simple request (including the 12 digit meter number) and the targeted meter will respond with most of its data.

To connect the request would be structured like: 2F 3F (12 Bytes Address) 30 30 21 0D 0A

Example: \x2F\x3F\x30\x30\x30\x33\x30\x30\x30\x30\x31\x31\x38\x34\x30\x30\x21\x0D\x0A

Request A Parameters for a v.4 Meter

Parameter Description
2F 3F
30 30 30 33 30 30 30 30 31 31 38 34 12 byte address = meter number: 000300001184
30 30 type of request. This hex code is for a type A v.4 meter request
21 0D 0A

Response from a v.4 Meter

Here is a typical type A response hex code from a v4 meter:

02 10 24 14 30 30 30 33 30 30 30 32 33 35 37 38 30 30 30 30 30 30 34 35 30 30 30 30 30 30 30 36 30 30 30 30 30 30 30 37 30 30 30 30 30 30 32 31 30 30 30 30 30 30 31 33 30 30 30 30 30 30 31 33 30 30 30 30 30 30 30 39 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 34 35 30 30 30 30 30 30 30 37 31 32 30 39 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 43 30 30 30 43 30 30 30 43 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 36 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 30 31 31 30 30 30 31 31 34 30 37 31 35 32 32 33 33 30 30 30 30 30 21 0d 0a 03 6a 41

Parameter Description
02 1 byte address = informs the computer that a packet is coming from meter ( reads )
10 24 2 byte address = model of meter. Example: EKM-Omnimeter Pulse v.4
14 1 byte address = firmware version
30 30 30 33 30 30 30 30 31 31 38 34 12 byte address = meter number: 000300001184
30 30 30 30 30 30 34 35 8 byte address = Total Kilowatt Hour
30 30 30 30 30 30 30 36 8 byte address = Total kVARh
30 30 30 30 30 30 30 37 8 byte address = Total Rev Kilowatt Hour
30 30 30 30 30 30 32 31 8 byte address = Total Kilowatt Hour L1
30 30 30 30 30 30 31 33 8 byte address = Total Kilowatt Hour L2
30 30 30 30 30 30 31 33 8 byte address = Total Kilowatt Hour L3
30 30 30 30 30 30 30 39 8 byte address = Reverse Kilowatt Hour L1
30 30 30 30 30 30 30 30 8 byte address = Reverse Kilowatt Hour L2
30 30 30 30 30 30 30 30 8 byte address = Reverse Kilowatt Hour L3
30 30 30 30 30 30 34 35 8 byte address = Resettable Kilowatt Hour Total
30 30 30 30 30 30 30 37 8 byte address = Resettable Reverse Kilowatt Hour
31 32 32 39 4 byte address = Volts L1
30 30 30 30 4 byte address = Volts L2
30 30 30 30 4 byte address = Volts L3
30 30 30 30 30 5 byte address = Amps L1
30 30 30 30 30 5 byte address = Amps L2
30 30 30 30 30 5 byte address = Amps L3
30 30 30 30 30 30 30 7 byte address = Watts L1
30 30 30 30 30 30 30 7 byte address = Watts L2
30 30 30 30 30 30 30 7 byte address = Watts L3
30 30 30 30 30 30 30 7 byte address = Total Watts
43 30 30 30 4 byte address = Cos L1
43 30 30 30 4 byte address = Cos L2
43 30 30 30 4 byte address = Cos L3
30 30 30 30 30 30 30 7 byte address = Var L1
30 30 30 30 30 30 30 7 byte address = Var L2
30 30 30 30 30 30 30 7 byte address = Var L3
30 30 30 30 30 30 30 7 byte address = Var L1, L2, L3
36 30 30 35 4 byte address = Frequency
30 30 30 30 30 30 30 30 8 byte address = Pulse 1
30 30 30 30 30 30 30 30 8 byte address = Pulse 2
30 30 30 30 30 30 30 30 8 byte address = Pulse 3
30 1 byte address = Indicates Pulse Input State High/Low. Example:
30 = (0) High/High/High,
31 = (1) High/High/Low,
32 = (2) High/Low/High,
33 = (3) High/Low/Low,
34 = (4) Low/High/High,
35 = (5) Low/High/Low,
36 = (6) Low/Low/High,
37 = (7) Low/Low/Low
31 1 byte address = Indicates direction of current for L1, L2, L3. Example:
31 = (1) Forward/Forward/Forward,
32 = (2) Forward/Forward/Reverse,
33 = (3) Forward/Reverse/Forward,
34 = (4) Reverse/Forward/Forward,
35 = (5) Forward/Reverse/Reverse,
36 = (6) Reverse/Forward/Reverse,
37 = (7) Reverse/Reverse/Forward,
38 = (8) Reverse/Reverse/Reverse
31 1 byte address = Indicates outputs. Example:
31 = (1) OFF/OFF,
32 = (2) OFF/ON,
33 = (3) ON/OFF,
34 = (4) ON/ON
30 1 byte address = Indicates Kilowatt Hour decimal places. Example:
30 = 0 decimal places,
31 = 1 decimal place,
32 = 2 decimal places
30 30 2 byte address = Reserved
31 31 34 30 37 31 35 32 32 33 33 30 30 30 14 byte address = Date and Time
30 30 2 byte address = type
21 0D 0A 03 4 byte address = End Session Request
6A 41 2 byte address = CRC-16 checksum

Here is a PDF of the breakdown of the send and response hex code.

RS-485 CRC-16 Checksum

CRC-16 Checksum


# EKM CRC-16 Table
CRCTABLE = [
        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
];


# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

def ekm_calc_crc16(buf)
        crc = 0xffff
        i = 0
        while i < buf.length  do
            c = buf[i].chr
            index = (crc ^ c.ord) & 0xff
            crct = CRCTABLE[index]
            crc=(crc>>8)^crct
            i +=1
        end

        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
        return crc
end     


# This is an example that will print the CRC value for a massage received FROM a V4 meter

crc=ekm_calc_crc16("\x10\x24\x15\x30\x30\x30\x33\x30\x30\x30\x30\x31\x31\x38\x34\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x32\x31\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x20\x31\x30\x30\x43\x30\x30\x30\x43\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x36\x30\x30\x30\x30\x30\x31\x30\x33\x31\x33\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x31\x30\x30\x30\x31\x37\x30\x35\x32\x34\x30\x34\x32\x33\x31\x34\x33\x37\x30\x30\x21\x0d\x0a\x03");

require 'pp'
pp crc.to_s(16)

# EKM CRC-16 Table
CRCTABLE = [
        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
];

# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

def ekm_calc_crc16(buf):
        crc = 0xffff
        for c in buf:
                index = (crc ^ ord( c )) & 0xff
                crct = CRCTABLE[index]
                crc=(crc>>8)^crct
        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
        return crc


# This is an example that will print the CRC value for a massage received FROM a V4 meter

crc=ekm_calc_crc16("\x10\x24\x15\x30\x30\x30\x33\x30\x30\x30\x30\x31\x31\x38\x34\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x32\x31\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x20\x31\x30\x30\x43\x30\x30\x30\x43\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x36\x30\x30\x30\x30\x30\x31\x30\x33\x31\x33\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x31\x30\x30\x30\x31\x37\x30\x35\x32\x34\x30\x34\x32\x33\x31\x34\x33\x37\x30\x30\x21\x0d\x0a\x03");
print(hex(crc));

<?php
// EKM CRC-16 Table
$CRCTABLE = array(
        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
);

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

function ekm_calc_crc16($buf){
    $crc = 0xffff;
    global $CRCTABLE;
    $len = strlen($buf);
    for($i=0; $i< $len; $i++){
        $c = $buf[$i];
        $index = ($crc ^ ord($c)) & 0xff;
        $crct = $CRCTABLE[$index];
        $crc=($crc>>8) ^ $crct;
    }
    $crc = ($crc << 8) | ($crc >> 8);
    $crc &= 0x7F7F;
    return $crc;
}

// This is an example that will print the CRC value for a massage received FROM a V4 meter

echo dechex(ekm_calc_crc16("\x10\x24\x15\x30\x30\x30\x33\x30\x30\x30\x30\x31\x31\x38\x34\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x32\x31\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x20\x31\x30\x30\x43\x30\x30\x30\x43\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x36\x30\x30\x30\x30\x30\x31\x30\x33\x31\x33\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x31\x30\x30\x30\x31\x37\x30\x35\x32\x34\x30\x34\x32\x33\x31\x34\x33\x37\x30\x30\x21\x0d\x0a\x03"));

?>
# EKM CRC-16 Table
use constant CRCTABLE => [
        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
];


# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

sub ekm_calc_crc16 {
     my $buf = shift(@_);
     my $crc=0xffff;
     my $s;

        my @arr = split("", $buf);
        for(my $i=0; $i<length($buf); $i++) {
          my $chr =  ord($arr[$i]);
          $s = ($crc ^ $chr) & 0xff;
          my $crct = CRCTABLE->[$s];
          $crc = ($crc>>8) ^ $crct;
        }
     $crc = ($crc << 8) | ($crc >> 8);
     $crc &= 0x7F7F;

     return $crc;
}


# This is an example call of the ekm_calc_crc16 function
# that will print the CRC value for a massage received FROM a V4 meter

printf ("%x",ekm_calc_crc16("\x10\x24\x15\x30\x30\x30\x33\x30\x30\x30\x30\x31\x31\x38\x34\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x32\x31\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x20\x31\x30\x30\x43\x30\x30\x30\x43\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x36\x30\x30\x30\x30\x30\x31\x30\x33\x31\x33\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x31\x30\x30\x30\x31\x37\x30\x35\x32\x34\x30\x34\x32\x33\x31\x34\x33\x37\x30\x30\x21\x0d\x0a\x03"));

/*
 Instructions to run this program

 1. Put this code in a file named ekmcrc.java
 2. Copy the downloaded org.json jar and EKM.java to the same directory
 3. Compile
  javac ekmcrc.java
 4. Run
  java ekmcrc
*/


// EKM CRC-16 Table
public class EKMChecksum {
    private static final int[] CRCTABLE = {
            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 };


// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");


    public static int ekm_calc_crc16(String buf) {
        int crc = 0xffff;
        if (buf != null) {

            char[] charArr = buf.toCharArray();
            for (int i = 0, len = charArr.length; i < len; i++) {
                int c = charArr[i];
                int index = (crc ^ c) & 0xff;
                int crct = CRCTABLE[index];
                crc = ((crc >> 8) ^ crct);
            }
            crc = (crc << 8) | (crc >> 8);
            crc &= 0x7F7F;
        }

        return crc;

    }

    public static void main(String[] args) {

// This is an example that will print the CRC value for a massage received FROM a V4 meter
// HEX 0D and 0A can not be typed using unicode so use \r for 0D and \n for 0A

        System.out.println(Integer.toHexString(ekm_calc_crc16("\u0010\u0024\u0015\u0030\u0030\u0030\u0033\u0030\u0030\u0030\u0030\u0031\u0031\u0038\u0034\u0030\u0030\u0030\u0030\u0030\u0030\u0032\u0036\u0030\u0030\u0030\u0030\u0030\u0030\u0031\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0032\u0036\u0030\u0030\u0030\u0030\u0030\u0030\u0032\u0036\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0032\u0036\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0031\u0032\u0031\u0034\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0020\u0031\u0030\u0030\u0043\u0030\u0030\u0030\u0043\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0036\u0030\u0030\u0030\u0030\u0030\u0031\u0030\u0033\u0031\u0033\u0034\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0030\u0031\u0031\u0030\u0030\u0030\u0031\u0037\u0030\u0035\u0032\u0034\u0030\u0034\u0032\u0033\u0031\u0034\u0033\u0037\u0030\u0030\u0021\r\n\u0003")));
    }
}

/*
 *  Saved this example code to a file named ekmcrc16.c
 *  - Compile
 *  gcc ekmcrc16.c -o ekmcrc16
 *  - Run
 *  ./ekmcrc16
 *
 */
// Required Includes

#include <stdio.h>

// EKM CRC-16 Table
static const unsigned short CRCTABLE[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
};

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

unsigned short ekm_calc_crc16(const char *ptr)
{

   unsigned short crc = 0xffff;
                while(*ptr){
                        crc = (crc >> 8) ^ CRCTABLE[(crc ^ *ptr) & 0xff];
                        ptr++;
                }
        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7f7f;

        return crc;
}

main()
{

        // This is an example that will print the CRC value for a massage received FROM a V4 meter

        const char inputStr[] = "\x10\x24\x15\x30\x30\x30\x33\x30\x30\x30\x30\x31\x31\x38\x34\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x32\x31\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x20\x31\x30\x30\x43\x30\x30\x30\x43\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x36\x30\x30\x30\x30\x30\x31\x30\x33\x31\x33\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x31\x30\x30\x30\x31\x37\x30\x35\x32\x34\x30\x34\x32\x33\x31\x34\x33\x37\x30\x30\x21\x0d\x0a\x03";
        printf("%04x",ekm_calc_crc16(inputStr));

}


// EKM CRC-16 Table
var CRCTABLE = [
        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
];

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

function ekm_calc_crc16(buf){
        var crc = 0xffff;
        for (var i = 0, len = buf.length; i < len; i++) {
          var c = buf[i];
          var index = (crc ^ c.charCodeAt( 0 )) & 0xff;
          var crct = CRCTABLE[index];
          crc=(crc>>8)^crct;
        }
        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
        return crc
}

// This is an example that will print the CRC value for a massage received FROM a V4 meter

console.log(ekm_calc_crc16("\x10\x24\x15\x30\x30\x30\x33\x30\x30\x30\x30\x31\x31\x38\x34\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x31\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x32\x36\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x32\x31\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x20\x31\x30\x30\x43\x30\x30\x30\x43\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x36\x30\x30\x30\x30\x30\x31\x30\x33\x31\x33\x34\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31\x31\x30\x30\x30\x31\x37\x30\x35\x32\x34\x30\x34\x32\x33\x31\x34\x33\x37\x30\x30\x21\x0d\x0a\x03").toString(16));


Once you have established a connection to the meter, you will want to start making use of the crc-16 checksum in communication with the meter. You do not need to validate the checksum on messages you receive from the meter, however, it is handy to verify that the message you received was complete. If the checksum fails then you can simply dump the message from the meter and send another request.

The second reasons for the checksum is, in order to send data to the meter you have to add the crc-16 checksum to your message, the meter will verify its correct before applying the command. Checksums are required to send instructions to the meter.

On messages you receive from the meter the checksum is calculated from the 2nd byte to the 3rd from the last byte.

For example when you receive a Type A response from a V4 meter you would get this:

1 Byte - 02 This first byte ( 02 ) indicates packet coming from meter. Not used to calculate CRC-16 Checksum
2 Bytes - 10 24 This is the meter version type. This is where the CRC-16 Checksum starts
1 Byte - 15 This is the meter Firmware
12 Bytes - 30 30 30 33 30 30 30 30 31 31 38 34 This is the meter Number
8 Bytes - 30 30 30 30 30 30 32 36 This is the Total Active Kilowatt Hour
8 Bytes - 30 30 30 30 30 30 31 30 This is the Total Kilo Volt Amperes Reactive Hours
8 Bytes - 30 30 30 30 30 30 32 36 This is the Total Rev.kWh
24 Bytes - 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 This is 3 phase Kilowatt Hour
24 Bytes - 30 30 30 30 30 30 32 36 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 This is 3 phase Rev.kWh
8 Bytes - 30 30 30 30 30 30 30 30 This is Resettable Kilowatt Hour
8 Bytes - 30 30 30 30 30 30 30 30 This is Resettable Reverse kWh
4 Bytes - 31 32 31 34 This is Volts L1
4 Bytes - 30 30 30 30 This is Volts L2
4 Bytes - 30 30 30 30 This is Volts L3
5 Bytes - 30 30 30 30 30 This is Amps L1
5 Bytes - 30 30 30 30 30 This is Amps L2
5 Bytes - 30 30 30 30 30 This is Amps L3
7 Bytes - 30 30 30 30 30 30 30 This is Watts L1
7 Bytes - 30 30 30 30 30 30 30 This is Watts L2
7 Bytes - 30 30 30 30 30 30 30 This is Watts L3
7 Bytes - 30 30 30 30 30 30 30 This is Total Watts
4 Bytes - 20 31 30 30 This is Cos0 L1
4 Bytes - 43 30 30 30 This is Cos0 L2
4 Bytes - 43 30 30 30 This is Cos0 L3
7 Bytes - 30 30 30 30 30 30 30 This is Var L1
7 Bytes - 30 30 30 30 30 30 30 This is Var L2
7 Bytes - 30 30 30 30 30 30 30 This is Var L3
7 Bytes - 30 30 30 30 30 30 30 This is Total Var
4 Bytes - 36 30 30 30 This is Frequency
8 Bytes - 30 30 31 30 33 31 33 34 This is Pulse Count 1
8 Bytes - 0 30 30 30 30 30 30 30 This is Pulse Count 2
8 Bytes - 30 30 30 30 30 30 30 30 This is Pulse Count 3
1 Byte - 30 This is Pulse Input Hi/Lo
1 Byte - 31 This is Direction of Current
1 Byte - 31 This is Outputs On/Off
1 Byte - 30 This is Kilowatt Hour Data Decimal Places
2 Bytes - 30 30 This is Reserved space
14 Bytes - 31 37 30 35 32 34 30 34 32 33 31 34 33 37 This is the Current Time
2 Bytes - 30 30 This is Type A response from Meter
4 Bytes - 21 0D 0A 03 This is the Close String. This is were the CRC-16 calculation of the checksum ends
2 Bytes - ?? ?? This is were the CRC-16 Checksum will be placed

Where the first byte ( 02 ) is skipped and the last 2 CRC-16 checksum bytes are not used when sending it to the ekm_calc_crc16 function.

For sending instructions to the meter the checksum starts from the 2nd byte and goes to the end of the instruction string, then you append the checksum to the end before sending it to the meter.

For example, the instruction would look like this: 01 52 31 02 30 30 31 32 03.

You would not include the first byte of 01, you would call the ekm_calc_crc16 function only for 52 31 02 30 30 31 32 03.

The CRC-16 function would then return a checksum of: 2e 65.

Now that you have the checksum value you will append it to the end of the instruction string: 52 31 02 30 30 31 32 03 2e 65.

The first byte of 01 will also be included back into the instruction string before it is sent to the meter: 01 52 31 02 30 30 31 32 03 2e 65

The completed instruction string of: 01 52 31 02 30 30 31 32 03 2e 65, is now ready to be sent to the meter.

The example instruction code above represents how a typical CRC-16 checksum will be generated by calling on the ekm_calc_crc16 function. The instruction code can be a response from a message received by a v4 meter.

RS-485 Sending Requests

Sending Requests to Meter


# Sending Meter Request Example (version 1.00)

# Requires the SerialPort gem
# (http://rubygems.org/gems/serialport)
# gem install serialport
require "serialport"

# EKM CRC-16 Table
CRCTABLE = [
        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
];


# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");
def ekm_calc_crc16(buf)
        crc = 0xffff
        i = 0
        while i < buf.length  do
            c = buf[i].chr
            index = (crc ^ c.ord) & 0xff
            crct = CRCTABLE[index]
            crc=(crc>>8)^crct
            i +=1
        end

        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
        return crc
end


# In this code example we make a simple function that will take the command
# you want to send to the meter and make the crc for it before sending.
# After sending we get the response from the meter and check its crc to make sure
# the response is correct
def send_rec(send)
        # get the the 2nd through the last byte
        # from the send string to create crc
        crc = ekm_calc_crc16(send[1..-1])

        # Convert CRC from int to byte and add to the send request
        send=send+((crc >> 8) & 0xFF).chr + (crc & 0xFF).chr

        # Send request
        $sp.write(send)

        #Set Timeout
        timeout = 5

        # Get meter response
        now=Time.now
        read=""
        response=""
        count=0

        while count<255
            read=$sp.getc
            if read
                 now=Time.now
                 count+=1
                 response.concat(read)
            end
            if Time.now > now + timeout
                 break
            end
        end

        if response==""
            print("Meter Instruction: \n")
            print(send.unpack('C*').map {|e| "%02X" % e}.join(" "))
            print("\nDid not trigger a response\n\n")
            return
        end

        # The meter returns HEX 06 if ok
        if(response[0]=="\x06")
            print("OK.\n\n")
        end    

        # If the meter returns HEX 02 it's the start of a text read
        if(response[0]=="\x02")
            # Get check CRC in response (byte 2 through the 3rd from the last)
            # to make sure it's valid
            crc =  ekm_calc_crc16(response[1..-3])

            # Compare our calculated CRC with the resonse CRC
            if (((crc >> 8) & 0xFF).chr + (crc & 0xFF).chr ==response[-2..-1])
                # Show response
                print(response.unpack('C*').map {|e| "%02X" % e}.join(" ") +"\n\n")
            else
                print("Meter response CRC failed\n\n")
            end    
        end    

end


# Params for serial port
# Change /dev/ttyUSB0 to match your system
port_str = "/dev/ttyUSB0"
baud_rate = 9600
data_bits = 7
stop_bits = 1
parity = SerialPort::EVEN
flow = SerialPort::NONE

# Open connection to serial port
$sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits, parity)
$sp.flow_control = flow

# Meter Number and Password
# Default password: 00000000
meter="000300001184"
password="00000000"

# In this example we have assigned
# some meter request strings to variables

# Get meter data A request and connection request
cmd_request_a="\x2F\x3F"+meter+"\x30\x30\x21\x0D\x0A"
# Get meter data B request
cmd_request_b="\x2F\x3F"+meter+"\x30\x31\x21\x0D\x0A"

# Get last 6 months (total kwh)
cmd_6mo_totkwh="\x01\x52\x31\x02\x30\x30\x31\x31\x03"
# Get last 6 months (reverse kwh)
cmd_6mo_revkwh="\x01\x52\x31\x02\x30\x30\x31\x32\x03"

# Send Password String
cmd_send_passwd="\x01\x50\x31\x02\x28"+password+"\x29\x03"

# Relay Open and Close Request Stings
cmd_relay_close_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x30\x30\x30\x30\x30\x29\x03"
cmd_relay_close_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x30\x30\x30\x30\x30\x29\x03"
cmd_relay_open_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x31\x30\x30\x30\x30\x29\x03"
cmd_relay_open_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x31\x30\x30\x30\x30\x29\x03"

# Close Connection String
cmd_close="\x01\x42\x30\x03\x75"

print("So let's give it a try...\n")
print("First we open the connection to the meter\n")
print("It will respond with the first 255 bytes of meter data (Request A)\n")
send_rec(cmd_request_a)

print("Now we can request other data\n")
print("For example the remaining 255 bytes of meter data (Request B)\n")
send_rec(cmd_request_b)

print("Or request things like the last 6 months of total kwh and reverse kwh\n")
send_rec(cmd_6mo_totkwh)
send_rec(cmd_6mo_revkwh)

print("When requesting information from the meter (reads)\n")
print("you can send as many requests as you like once you sent\n")
print("the first connection request (Request A)\n\n")

print("Now let's try sending a setting to the meter\n")
print("Unlike reading information from the meter\n")
print("You have to send the connection string and password\n")
print("before each setting request\n\n")

print("Now we will loop 3 times opening and closing the relays\n")
for x in 1..3
        print("Send Connection Request A\n")
        send_rec(cmd_request_a)

        print("Send Password\n")
        send_rec(cmd_send_passwd)

        print("Open Relay A\n")
        send_rec(cmd_relay_open_a)

        print("Send Connection Request A\n")
        send_rec(cmd_request_a)

        print("Send Password\n")
        send_rec(cmd_send_passwd)

        print("Close Relay A\n")
        send_rec(cmd_relay_close_a)

        print("Send Connection Request A\n")
        send_rec(cmd_request_a)

        print("Send Password\n")
        send_rec(cmd_send_passwd)

        print("Open Relay B\n")
        send_rec(cmd_relay_open_b)

        print("Send Connection Request A\n")
        send_rec(cmd_request_a)

        print("Send Password\n")
        send_rec(cmd_send_passwd)

        print("Close Relay B\n")
        send_rec(cmd_relay_close_b)
end        

print("When you're finished you will close the connection\n")
send_rec(cmd_close)

# Close connection to serial port
$sp.close

# Sending Meter Request Example (version 1.00)

# Requires pyserial module
# pip3 install pyserial

import serial

# EKM CRC-16 Table
CRCTABLE = [
        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
];

# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

def ekm_calc_crc16(buf):
        crc = 0xffff
        for c in buf:
                index = (crc ^ ord( c )) & 0xff
                crct = CRCTABLE[index]
                crc=(crc>>8)^crct
        crc = (crc << 8) | (crc >> 8)
        crc &= 0x7F7F
        return crc


# In this code example we make a simple function that will take the command
# you want to send to the meter and make the crc for it before sending.
# After sending we get the response from the meter and check its crc to make sure
# the response is correct

def send_rec(send):
        # get the the 2nd through the last byte
        # from the send string to create crc
        crc = ekm_calc_crc16(send[1:])

        # Convert CRC from int to byte and add to the send request
        send=send+chr((crc >> 8) & 0xFF) + chr(crc & 0xFF)
        # Send request
        sp.write(send.encode())
        # Get meter response
        response = sp.read(255)
        response=str(response,'ascii')

        if response=="":
                print("Meter Instruction: ")
                print(" ".join("{0:02x}".format(ord( c )) for c in send))
                print("Did not trigger a response\n\n")
                return

        # The meter returns HEX 06 if ok
        if response[0]=="\x06":
                print("OK.\n\n")
        # If the meter returns HEX 02 it's the start of a text read
        if response[0]=="\x02":
                # Get check CRC in response (byte 2 through the 3rd from the last)
                # to make sure it's valid
                crc =  ekm_calc_crc16(response[1:-2])

                # Compare our calculated CRC with the resonse CRC
                if chr((crc >> 8) & 0xFF) + chr(crc & 0xFF)==response[-2:]:
                        # Show response
                        print(" ".join("{0:02x}".format(ord( c )) for c in response)+"\n\n")
                else:
                        print("Meter response CRC failed\n\n")

# Open connection to serial port
# Change /dev/ttyUSB0 to match your system
sp = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=9600,
    parity=serial.PARITY_EVEN,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.SEVENBITS,
    xonxoff=0,
    timeout=5
)

# Meter Number and Password
# Default password: 00000000
meter="000300001184"
password="00000000"

# In this example we have assigned
# some meter request strings to variables

# Get meter data A request and connection request
cmd_request_a="\x2F\x3F"+meter+"\x30\x30\x21\x0D\x0A"
# Get meter data B request
cmd_request_b="\x2F\x3F"+meter+"\x30\x31\x21\x0D\x0A"

# Get last 6 months (total kwh)
cmd_6mo_totkwh="\x01\x52\x31\x02\x30\x30\x31\x31\x03"
# Get last 6 months (reverse kwh)
cmd_6mo_revkwh="\x01\x52\x31\x02\x30\x30\x31\x32\x03"

# Send Password String
cmd_send_passwd="\x01\x50\x31\x02\x28"+password+"\x29\x03"

# Relay Open and Close Request Stings
cmd_relay_open_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x30\x30\x30\x30\x30\x29\x03"
cmd_relay_open_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x30\x30\x30\x30\x30\x29\x03"
cmd_relay_close_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x31\x30\x30\x30\x30\x29\x03"
cmd_relay_close_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x31\x30\x30\x30\x30\x29\x03"

# Close Connection String
cmd_close="\x01\x42\x30\x03\x75"

print("So let's give it a try...")
print("First we open the connection to the meter")
print("It will respond with the first 255 bytes of meter data (Request A)")
send_rec(cmd_request_a)

print("Now we can request other data")
print("For example the remaining 255 bytes of meter data (Request B)")
send_rec(cmd_request_b)

print("Or request things like the last 6 months of total kwh and reverse kwh")
send_rec(cmd_6mo_totkwh)
send_rec(cmd_6mo_revkwh)

print("When requesting information from the meter (reads)")
print("you can send as many requests as you like once you sent")
print("the first connection request (Request A)\n\n")

print("Now let's try sending a setting to the meter")
print("Unlike reading information from the meter")
print("You have to send the connection string and password")
print("before each setting request\n\n")

print("Now we will loop 3 times opening and closing the relays")
for x in range(1,3):
        print("Send Connection Request A")
        send_rec(cmd_request_a)

        print("Send Password")
        send_rec(cmd_send_passwd)

        print("Open Relay A")
        send_rec(cmd_relay_open_a)

        print("Send Connection Request A")
        send_rec(cmd_request_a)

        print("Send Password")
        send_rec(cmd_send_passwd)

        print("Close Relay A")
        send_rec(cmd_relay_close_a)

        print("Send Connection Request A")
        send_rec(cmd_request_a)

        print("Send Password")
        send_rec(cmd_send_passwd)

        print("Open Relay B")
        send_rec(cmd_relay_open_b)

        print("Send Connection Request A")
        send_rec(cmd_request_a)

        print("Send Password")
        send_rec(cmd_send_passwd)

        print("Close Relay B")
        send_rec(cmd_relay_close_b)


print("When you're finished you will close the connection")
send_rec(cmd_close)

# Close connection to serial port
sp.close

<?php
// Sending Meter Request Example (version 1.00)

// Requires the pecl_dio extenion
// On CentOS
// yum install php-pear
// pecl install dio

// EKM CRC-16 Table
$CRCTABLE = array(
        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
);

// Open connection to serial port
// Change /dev/ttyUSB0 to match your system
$sp = dio_open( '/dev/ttyUSB0', O_RDWR | O_NONBLOCK );

// Configure the port
dio_tcsetattr( $sp, array(
   'baud'         => 9600,
   'bits'         => 7,
   'stop'         => 1,
   'parity'       => 2,
   'flow_control' => 0,
   'is_canonical' => 0
) );


// Meter Number and Password
// Default password: 00000000
$meter="000300001184";
$password="00000000";

// In this example we have assigned
// some meter request strings to variables

// Get meter data A request and connection request
$cmd_request_a="\x2F\x3F".$meter."\x30\x30\x21\x0D\x0A";
// Get meter data B request
$cmd_request_b="\x2F\x3F".$meter."\x30\x31\x21\x0D\x0A";

// Get last 6 months (total kwh)
$cmd_6mo_totkwh="\x01\x52\x31\x02\x30\x30\x31\x31\x03";
// Get last 6 months (reverse kwh)
$cmd_6mo_revkwh="\x01\x52\x31\x02\x30\x30\x31\x32\x03";

// Send Password String
$cmd_send_passwd="\x01\x50\x31\x02\x28".$password."\x29\x03";

// Relay Open and Close Request Stings
$cmd_relay_open_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x30\x30\x30\x30\x30\x29\x03";
$cmd_relay_open_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x30\x30\x30\x30\x30\x29\x03";
$cmd_relay_close_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x31\x30\x30\x30\x30\x29\x03";
$cmd_relay_close_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x31\x30\x30\x30\x30\x29\x03";

// Close Connection String
$cmd_close="\x01\x42\x30\x03\x75";

echo "So let's give it a try...\n";
echo "First we open the connection to the meter\n";
echo "It will respond with the first 255 bytes of meter data (Request A)\n";
send_rec($cmd_request_a);

echo "Now we can request other data\n";
echo "For example the remaining 255 bytes of meter data (Request B)\n";
send_rec($cmd_request_b);

echo "Or request things like the last 6 months of total kwh and reverse kwh\n";
send_rec($cmd_6mo_totkwh);
send_rec($cmd_6mo_revkwh);

echo "When requesting information from the meter (reads)\n";
echo "you can send as many requests as you like once you sent\n";
echo "the first connection request (Request A)\n\n\n";

echo "Now let's try sending a setting to the meter\n";
echo "Unlike reading information from the meter\n";
echo "You have to send the connection string and password\n";
echo "before each setting request\n\n\n";

echo "Now we will loop 3 times opening and closing the relays\n";
for ($x=0; $x<3; $x++){
        echo "Send Connection Request A\n";
        send_rec($cmd_request_a);

        echo "Send Password\n";
        send_rec($cmd_send_passwd);

        echo "Open Relay A\n";
        send_rec($cmd_relay_open_a);

        echo "Send Connection Request A\n";
        send_rec($cmd_request_a);

        echo "Send Password\n";
        send_rec($cmd_send_passwd);

        echo "Close Relay A\n";
        send_rec($cmd_relay_close_a);

        echo "Send Connection Request A\n";
        send_rec($cmd_request_a);

        echo "Send Password\n";
        send_rec($cmd_send_passwd);        

        echo "Open Relay B\n";
        send_rec($cmd_relay_open_b);

        echo "Send Connection Request A\n";
        send_rec($cmd_request_a);

        echo "Send Password\n";
        send_rec($cmd_send_passwd);

        echo "Close Relay B\n";
        send_rec($cmd_relay_close_b);
}

echo "When you're finished you will close the connection\n";
send_rec($cmd_close);

// Close connection to serial port
dio_close( $sp );


// In this code example we make a simple function that will take the command
// you want to send to the meter and make the crc for it before sending.
// After sending we get the response from the meter and check its crc to make sure
// the response is correct

function send_rec($send){
        global $sp;

        // get the the 2nd through the last byte
        // from the send string to create crc
        $crc = ekm_calc_crc16(substr($send,1));

        // Convert CRC from int to byte and add to the send request
        $send=$send.chr(($crc >> 8) & 0xFF).chr($crc & 0xFF);

        // Send request
        dio_write($sp,$send);

        // Set Timeout
        $timeout = 5;

        // Get meter response        
        $read = '';
        $response = '';
        $now=time();
        while( strlen($response)<255 && time()<$now+$timeout) {
                $read = dio_read($sp,1);
                $response .= $read;
        }

        if ($response==""){                
                echo "Meter Instruction: \n";
                echo strToHex($send)."\n";
                echo "Did not trigger a response\n\n";
                return;
        }

        // The meter returns HEX 06 if ok
        if (substr($response,0,1)=="\x06")
                echo "OK.\n\n";
        // If the meter returns HEX 02 it's the start of a text read
        if (substr($response,0,1)=="\x02"){
                // Get check CRC in response (byte 2 through the 3rd from the last)
                // to make sure it's valid
                $crc =  ekm_calc_crc16(substr($response,1,-2));
                // Compare our calculated CRC with the resonse CRC
                if (chr(($crc >> 8) & 0xFF).chr($crc & 0xFF)==substr($response,-2))
                        // Show response
                        echo strToHex($response)."\n\n";
                else
                        echo "Meter response CRC failed\n\n";
        }
}

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

function ekm_calc_crc16($buf){
        $crc = 0xffff;
        global $CRCTABLE;
        $len = strlen($buf);
        for($i=0; $i< $len; $i++){
                $c = $buf[$i];
                $index = ($crc ^ ord($c)) & 0xff;
                $crct = $CRCTABLE[$index];
                $crc=($crc>>8) ^ $crct;
        }
        $crc = ($crc << 8) | ($crc >> 8);
        $crc &= 0x7F7F;
        return $crc;
}



function strToHex($buf)
{
    $hex='';
    for ($i=0; $i < strlen($buf); $i++)
    {
        $hex .= sprintf("%02X",ord($buf[$i])).' ';
    }
    return $hex;
}

?>

# Sending Meter Request Example (version 1.00)

# Requires the Device::SerialPort module
# On CentOS
# yum install perl-Device-SerialPort

use Device::SerialPort;

# EKM CRC-16 Table
use constant CRCTABLE => [
        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
];


# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

sub ekm_calc_crc16 {
     my $buf = shift(@_);
     my $crc=0xffff;
     my $s;

        my @arr = split("", $buf);
        for(my $i=0; $i<length($buf); $i++) {
          my $chr =  ord($arr[$i]);
          $s = ($crc ^ $chr) & 0xff;
          my $crct = CRCTABLE->[$s];
          $crc = ($crc>>8) ^ $crct;
        }
     $crc = ($crc << 8) | ($crc >> 8);
     $crc &= 0x7F7F;

     return $crc;
}


# In this code example we make a simple function that will take the command
# you want to send to the meter and make the crc for it before sending.
# After sending we get the response from the meter and check its crc to make sure
# the response is correct
sub send_rec{
    my $send = shift(@_);
    # get the the 2nd through the last byte
    # from the send string to create crc
    my $crc = ekm_calc_crc16(substr($send, 1)) ;

    # Convert CRC from int to byte and add to the send request
    $send=$send.chr(($crc >> 8) & 0xFF).chr($crc & 0xFF);

    # Send request
    $sp->write($send);

    #Set Timeout
    my $timeout = 5;

    # Get meter response
    my $count=0;
    my $response="";
    while ($timeout>0 && $count<255) {
        my ($chars,$read)=$sp->read(255);
            if ($chars > 0) {
                $count+=$chars;
                $response.=$read;
            }
            else {
                $timeout--;
            }
        }


    if(length($response)==0){
        print("Meter Instruction\n" );
        # Show response as HEX
        $send =~ s/(.)/sprintf("%02X",ord($1))." "/seg;
        print $send;
        print("\nDid not trigger a response\n\n");
        return;
    }


    # The meter returns HEX 06 if ok
    if(substr($response,0,1) eq "\x06"){
        print("OK.\n\n");
    }    

    # If the meter returns HEX 02 it's the start of a text read
    if(substr($response,0,1) eq "\x02" && length($response) ==255){
        # Get check CRC in response (byte 2 through the 3rd from the last)
        # to make sure it's valid
        $crc = ekm_calc_crc16(substr($response,1,-2));

        # Compare our calculated CRC with the resonse CRC
        if ((chr(($crc >> 8) & 0xFF).chr($crc & 0xFF)) ==substr($response,-2)){
            # Show response
            $response =~ s/(.)/sprintf("%02X",ord($1))." "/seg;
            print $response."\n\n";
        }    
        else{
            print "Meter response CRC failed\n\n";
        }    

    }    

}


# Params for serial port
# Change /dev/ttyUSB0 to match your system
# Open connection to serial port
$sp = new Device::SerialPort("/dev/ttyUSB0");
# Configure the port
$sp->user_msg(ON);
$sp->baudrate(9600);
$sp->parity("even");
$sp->databits(7);
$sp->stopbits(1);
$sp->handshake("xoff");
$sp->write_settings;
# don't wait for each character
$sp->read_char_time(0);     
# wait for 1 second per unfulfilled read
$sp->read_const_time(1000);


# Meter Number and Password
# Default password: 00000000
my $meter="000300001184";
my $password="00000000";

# In this example we have assigned
# some meter request strings to variables

# Get meter data A request and connection request
my $cmd_request_a="\x2F\x3F$meter\x30\x30\x21\x0D\x0A";
# Get meter data B request
my $cmd_request_b="\x2F\x3F$meter\x30\x31\x21\x0D\x0A";

# Get last 6 months (total kwh)
my $cmd_6mo_totkwh="\x01\x52\x31\x02\x30\x30\x31\x31\x03";
# Get last 6 months (reverse kwh)
my $cmd_6mo_revkwh="\x01\x52\x31\x02\x30\x30\x31\x32\x03";

# Send Password String
my $cmd_send_passwd="\x01\x50\x31\x02\x28$password\x29\x03";

# Relay Open and Close Request Stings
my $cmd_relay_open_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x30\x30\x30\x30\x30\x29\x03";
my $cmd_relay_open_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x30\x30\x30\x30\x30\x29\x03";
my $cmd_relay_close_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x31\x30\x30\x30\x30\x29\x03";
my $cmd_relay_close_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x31\x30\x30\x30\x30\x29\x03";

# Close Connection String
my $cmd_close="\x01\x42\x30\x03\x75";

print("So let's give it a try...\n");
print("First we open the connection to the meter\n");
print("It will respond with the first 255 bytes of meter data (Request A)\n");
send_rec($cmd_request_a);

print("Now we can request other data\n");
print("For example the remaining 255 bytes of meter data (Request B)\n");
send_rec($cmd_request_b);

print("Or request things like the last 6 months of total kwh and reverse kwh\n");
send_rec($cmd_6mo_totkwh);
send_rec($cmd_6mo_revkwh);

print("When requesting information from the meter (reads)\n");
print("you can send as many requests as you like once you sent\n");
print("the first connection request (Request A)\n\n");

print("Now let's try sending a setting to the meter\n");
print("Unlike reading information from the meter\n");
print("You have to send the connection string and password\n");
print("before each setting request\n\n");

print("Now we will loop 3 times opening and closing the relays\n");
for my $x (1..3){
        print("Send Connection Request A\n");
        send_rec($cmd_request_a);

        print("Send Password\n");
        send_rec($cmd_send_passwd);

        print("Open Relay A\n");
        send_rec($cmd_relay_open_a);

        print("Send Connection Request A\n");
        send_rec($cmd_request_a);

        print("Send Password\n");
        send_rec($cmd_send_passwd);

        print("Close Relay A\n");
        send_rec($cmd_relay_close_a);

        print("Send Connection Request A\n");
        send_rec($cmd_request_a);

        print("Send Password\n");
        send_rec($cmd_send_passwd);

        print("Open Relay B\n");
        send_rec($cmd_relay_open_b);

        print("Send Connection Request A\n");
        send_rec($cmd_request_a);

        print("Send Password\n");
        send_rec($cmd_send_passwd);

        print("Close Relay B\n");
        send_rec($cmd_relay_close_b);
}    

print("When you're finished you will close the connection\n");
send_rec($cmd_close);

# Close connection to serial port
$sp->close || die "failed to close";
undef $sp;

/*
 Sending Meter Request Example (version 1.01)

 Download the jssc.jar (java-simple-serial-connector)
 from: https://code.google.com/archive/p/java-simple-serial-connector/

 Instructions to run this program

 1. Put this code in a file named connect.java
 2. Copy the downloaded jssc.jar and connect.java to the same directory
 3. Compile
  javac -cp .:./jssc.jar ./connect.java
 4. Run
  java -cp .:./jssc.jar connect
*/

// Import required classes
import jssc.*;

public class connect {

static SerialPort serialPort;
// EKM CRC-16 Table
private static final int[] CRCTABLE = {
    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 };

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");


public static int ekm_calc_crc16(String buf) {
    int crc = 0xffff;
    if (buf != null) {

        char[] charArr = buf.toCharArray();
        for (int i = 0, len = charArr.length; i < len; i++) {
            int c = charArr[i];
            int index = (crc ^ c) & 0xff;
            int crct = CRCTABLE[index];
            crc = ((crc >> 8) ^ crct);
        }
        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
    }

    return crc;

}

// In this code example we make a simple function that will take the command
// you want to send to the meter and make the crc for it before sending.
// After sending we get the response from the meter and check its crc to make sure
// the response is correct

public static void send_rec(String send) throws SerialPortException{
    String firstByte;
    String response;
    byte[] buffer = "\r\n".getBytes();

    // get the the 2nd through the last byte
    // from the send string to create crc
    int crc = ekm_calc_crc16(send.substring(1));
    send=send+(char)((crc >> 8) & 0xFF)+(char)(crc & 0xFF);

    // Send request
    serialPort.writeBytes(send.getBytes());

    // Set connection timeout;
    int timeout=5;

    // Get byte 1 of meter response
    try {
    firstByte = serialPort.readString(1,timeout*1000);
    } catch (SerialPortTimeoutException e1)
    {
            System.out.println("Meter Instruction: ");
            System.out.println(byteArrayToHex(send)+"");
            System.out.println("Did not trigger a response\n");
            return;
    }

    // The meter returns HEX 06 if ok
    if ((char)firstByte.charAt(0)==(char)(6 & 0xFF)){
        System.out.println("OK.\n");
        // Clear response buffer
        serialPort.purgePort(SerialPort.PURGE_RXCLEAR | SerialPort.PURGE_TXCLEAR);
        return;
    }

    // Get rest of meter response    
    try {
    buffer  = serialPort.readBytes(254,timeout*1000);
    } catch (SerialPortTimeoutException e1)
    {
            System.out.println("Did receive complete response\n");
            // Clear response buffer
            serialPort.purgePort(SerialPort.PURGE_RXCLEAR | SerialPort.PURGE_TXCLEAR);
            return;
    }
    response = new String(buffer);

    // Clear response buffer
    serialPort.purgePort(SerialPort.PURGE_RXCLEAR | SerialPort.PURGE_TXCLEAR);

    // If the meter returns HEX 02 it's the start of a text read
    if ((char)firstByte.charAt(0) == (char)(2 & 0xFF)){

            // Put 02 back in the response beings it
            // was pulled out when testing the first byte
            response=""+(char)(2)+response;

            // Get check CRC in response (byte 2 through the 3rd from the last)
            // to make sure it's valid
            crc =  ekm_calc_crc16(response.substring(1, 253));
            // Compare our calculated CRC with the resonse CRC
            if ((char)((crc >> 8) & 0xFF)==(char)response.charAt(253) && (char)(crc & 0xFF)==(char)response.charAt(254)){
                // Show response
                System.out.println(byteArrayToHex(response)+"\n");
            }        
            else{
                System.out.println( "Meter response CRC failed\n");
            }        
    }
}

public static void main(String[] args) {
    String response = "";
    byte[] buffer = "\r\n".getBytes();
    serialPort = new SerialPort("/dev/ttyUSB0");
    try {
        // Open connection to serial port
        // Change /dev/ttyUSB0 to match your system

        serialPort.openPort();
        // Configure the port
        serialPort.setParams(SerialPort.BAUDRATE_9600,
                             SerialPort.DATABITS_7,
                             SerialPort.STOPBITS_1,
                             SerialPort.PARITY_EVEN);
        serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);


        // Meter Number and Password
        // Default password: 00000000
        String meter="000300001184";
        String password="00000000";

        // In this example we have assigned
        // some meter request strings to variables

        // Get meter data A request and connection request
        String cmd_request_a="\u002F\u003F"+meter+"\u0030\u0030\u0021\r\n";
        // Get meter data B request
        String cmd_request_b="\u002F\u003F"+meter+"\u0030\u0031\u0021\r\n";

        // Get last 6 months (total kwh)
        String cmd_6mo_totkwh="\u0001\u0052\u0031\u0002\u0030\u0030\u0031\u0031\u0003";
        // Get last 6 months (reverse kwh)
        String cmd_6mo_revkwh="\u0001\u0052\u0031\u0002\u0030\u0030\u0031\u0032\u0003";

        // Send Password String
        String cmd_send_passwd="\u0001\u0050\u0031\u0002\u0028"+password+"\u0029\u0003";

        // Relay Open and Close Request Stings
        String cmd_relay_open_b="\u0001\u0057\u0031\u0002\u0030\u0030\u0038\u0032\u0028\u0030\u0030\u0030\u0030\u0030\u0029\u0003";
        String cmd_relay_open_a="\u0001\u0057\u0031\u0002\u0030\u0030\u0038\u0031\u0028\u0030\u0030\u0030\u0030\u0030\u0029\u0003";
        String cmd_relay_close_a="\u0001\u0057\u0031\u0002\u0030\u0030\u0038\u0031\u0028\u0031\u0030\u0030\u0030\u0030\u0029\u0003";
        String cmd_relay_close_b="\u0001\u0057\u0031\u0002\u0030\u0030\u0038\u0032\u0028\u0031\u0030\u0030\u0030\u0030\u0029\u0003";

        // Close Connection String
        String cmd_close="\u0001\u0042\u0030\u0003\u0075";

        System.out.println("So let's give it a try...");
        System.out.println("First we open the connection to the meter");
        System.out.println("It will respond with the first 255 bytes of meter data (Request A)");
        send_rec(cmd_request_a);

        System.out.println("Now we can request other data");
        System.out.println("For example the remaining 255 bytes of meter data (Request B)");
        send_rec(cmd_request_b);

        System.out.println("Or request things like the last 6 months of total kwh and reverse kwh");
        send_rec(cmd_6mo_totkwh);
        send_rec(cmd_6mo_revkwh);

        System.out.println("When requesting information from the meter (reads)");
        System.out.println("you can send as many requests as you like once you sent");
        System.out.println("the first connection request (Request A)\n\n");

        System.out.println("Now let's try sending a setting to the meter");
        System.out.println("Unlike reading information from the meter");
        System.out.println("You have to send the connection string and password");
        System.out.println("before each setting request\n\n");

        System.out.println("Now we will loop 3 times opening and closing the relays");
        for (int x=0; x<3; x++){
                System.out.println("Send Connection Request A");
                send_rec(cmd_request_a);

                System.out.println("Send Password");
                send_rec(cmd_send_passwd);

                System.out.println("Open Relay A");
                send_rec(cmd_relay_open_a);

                pause(5000);

                System.out.println("Send Connection Request A");
                send_rec(cmd_request_a);

                System.out.println("Send Password");
                send_rec(cmd_send_passwd);

                System.out.println("Close Relay A");
                send_rec(cmd_relay_close_a);

                pause(5000);

                System.out.println("Send Connection Request A");
                send_rec(cmd_request_a);

                System.out.println("Send Password");
                send_rec(cmd_send_passwd);

                System.out.println("Open Relay B");
                send_rec(cmd_relay_open_b);

                pause(5000);

                System.out.println("Send Connection Request A");
                send_rec(cmd_request_a);

                System.out.println("Send Password");
                send_rec(cmd_send_passwd);

                System.out.println("Close Relay B");
                send_rec(cmd_relay_close_b);

                pause(5000);
        }

        System.out.println("When you're finished you will close the connection");
        send_rec(cmd_close);

        // Close connection to serial port
        serialPort.closePort();

    }   catch (SerialPortException ex) {
        System.out.println(ex);
    }
}

public static void pause(int Pause) {
   try {
      Thread.sleep(Pause);
   }
   catch(InterruptedException ie){
      System.out.println("Thread interrupted !" + ie);
   }
   return;
}

public static String byteArrayToHex(String OrgStr) {
   byte[] a = OrgStr.getBytes();
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a){
      sb.append(String.format("%02x", b & 0xff));
      sb.append(" ");
   }
   return sb.toString();
}

}

/*
 *  Sending Meter Request Example (version 1.00)
 *
 *  Saved this example code to a file named connect.c
 *  - Compile
 *  gcc connect.c -o connect
 *  - Run
 *  ./connect
 */
// Required Includes

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>   /* File Control Definitions */
#include <termios.h> /* POSIX Terminal Control Definitions */
#include <unistd.h>  /* UNIX Standard Definitions */
#include <errno.h>   /* ERROR Number Definitions */
#include <string.h>

int sp;
// EKM CRC-16 Table
static const unsigned short CRCTABLE[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
};

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");
unsigned short ekm_calc_crc16(const char *ptr)
{
    unsigned short crc = 0xffff;
    while(*ptr){
        crc = (crc >> 8) ^ CRCTABLE[(crc ^ *ptr) & 0xff];
        ptr++;
    }
    crc = (crc << 8) | (crc >> 8);
    crc &= 0x7f7f;
    return crc;
}

char *substring(char *string, int position, int length)
{
    char *pointer;
    int c;
    pointer = malloc(length+1);
    if (pointer == NULL){
       printf("Unable to allocate memory.\n");
       exit(1);
    }

    for (c = 0 ; c < length ; c++){
       *(pointer+c) = *(string+position);
       string++;
    }
    *(pointer+c) = '\0';
    return pointer;
}

const char* chr(int i)
{
    static char str[1];
    char c = i;
    sprintf(str,"%c", c);
    return str;
}



char* strToHex(const char *ptr){
    char * hex ;
    if((hex = malloc(strlen(ptr)*2+1)) != NULL){
        hex[0] = '\0';
    }

    while(*ptr){
        char tmp[2];
        sprintf(tmp, "%02X ", (unsigned int) *ptr++);
        strcat(hex,tmp);
    }
    return hex;
}

char *concatenate_string(const char *str1,const char *str2)
{
   char *str3;
   int i=0;

   while(str1[i]){
      str3[i]=str1[i];
      i++;
  }
  int k =0;
  while(str2[k]){
      str3[i]=str2[k];
      i++;
      k++;
  }
  str3[i]='\0';
  return str3;
}                


// In this code example we make a simple function that will take the command
// you want to send to the meter and make the crc for it before sending.
// After sending we get the response from the meter and check its crc to make sure
// the response is correct
void send_rec(char send[]){
    char *ss = send;
    // get the the 2nd through the last byte
    // from the send string to create crc
    int crc = ekm_calc_crc16(substring(ss,1, strlen(ss)-1));

    // Convert CRC from int to byte and add to the send request
    ss =concatenate_string(ss,chr((crc >> 8) & 0xFF));
    ss =concatenate_string(ss,chr(crc & 0xFF));

    write(sp,ss,strlen(ss));

    // Get meter response
    char response[255];
    char firstByte[1];
    read(sp, &firstByte[0], sizeof(char));

    if (strcmp(firstByte,"") == 0){
        printf("Meter Instruction: \n");
        printf("%s \n", strToHex(ss));
        printf("Did not trigger a response\n\n");
        return;
    }

    // The meter returns HEX 06 if ok
    if (strcmp(substring(firstByte,0,1),"\x06") == 0){
         printf("OK.\n\n");
    }        

    // If the meter returns HEX 02 it's the start of a text read
    if (strcmp(substring(firstByte,0,1),"\x02") == 0){
      response[0] = firstByte[0];
      // Get meter response
      int a =1;
      while(a<255){
          read(sp, &response[a++], sizeof(char));
      }   
      // Get check CRC in response (byte 2 through the 3rd from the last)
      // to make sure it's valid
      crc =  ekm_calc_crc16(substring(response,1,strlen(response)-3));
      // Compare our calculated CRC with the resonse CRC
      if ( strcmp(chr((crc >> 8) & 0xFF), substring(response,strlen(response)-2,1) ) ==0 && strcmp(chr(crc & 0xFF), substring(response,strlen(response)-1,1) ) ==0){
          // Show response
          printf("%s \n\n", strToHex(response));
      }
      else{
          printf("Meter response CRC failed\n\n");
      }
    }
}    

void main(void){
    // Open connection to serial port
    // Change /dev/ttyUSB0 to match your system
    sp = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY );
    // Error Checking
    if(sp == -1){
       perror("Error! in Opening /dev/ttyUSB0");
       exit(-1);
    }   

    // Setting the Attributes of the serial port using termios structure
    struct termios SerialPortSettings;

    //Get the current attributes of the Serial port
    tcgetattr(sp, &SerialPortSettings);

    //Setting the Baud rate
    cfsetispeed(&SerialPortSettings,B9600); /* Set Read  Speed as 9600 */
    cfsetospeed(&SerialPortSettings,B9600); /* Set Write Speed as 9600 */

    SerialPortSettings.c_cflag |=  PARENB;
    SerialPortSettings.c_cflag &= ~CSTOPB;
    SerialPortSettings.c_cflag &= ~CSIZE;
    SerialPortSettings.c_cflag |=  CS7;
    SerialPortSettings.c_cflag &= ~CRTSCTS;
    SerialPortSettings.c_iflag |= IXOFF;
    SerialPortSettings.c_iflag &= ~ICANON;


    // Read buffer size
    SerialPortSettings.c_cc[VMIN] = 0;

    // Set connection timeout
    int timeout = 5;
    SerialPortSettings.c_cc[VTIME] = timeout*10;
    // apply new serial port settings
    tcsetattr(sp,TCSANOW,&SerialPortSettings);
    // discards old data in the buffer
    tcflush(sp, TCIOFLUSH);

    // Meter Number and Password
    // Default password: 00000000
    char *meter="000300001184";
    char *password="00000000";

    // In this example we have assigned
    // some meter request strings to variables
    // Get meter data A request and connection request
    char cmd_request_a[19];
    sprintf(cmd_request_a, "\x2F\x3F%s\x30\x30\x21\x0D\x0A", meter);


    // Get meter data B request
    char cmd_request_b[19];
    sprintf(cmd_request_b, "\x2F\x3F%s\x30\x31\x21\x0D\x0A", meter);

    // Get last 6 months (total kwh)
    char *cmd_6mo_totkwh="\x01\x52\x31\x02\x30\x30\x31\x31\x03";
    // Get last 6 months (reverse kwh)
    char *cmd_6mo_revkwh="\x01\x52\x31\x02\x30\x30\x31\x32\x03";

    // Send Password String
    char cmd_send_passwd[15];
    sprintf(cmd_send_passwd, "\x01\x50\x31\x02\x28%s\x29\x03", password);

    // Relay Open and Close Request Stings
    char *cmd_relay_open_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x30\x30\x30\x30\x30\x29\x03";
    char *cmd_relay_open_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x30\x30\x30\x30\x30\x29\x03";
    char *cmd_relay_close_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x31\x30\x30\x30\x30\x29\x03";
    char *cmd_relay_close_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x31\x30\x30\x30\x30\x29\x03";

    // Close Connection String
    char *cmd_close="\x01\x42\x30\x03\x75";


    printf("So let's give it a try...\n");
    printf("First we open the connection to the meter\n");
    printf("It will respond with the first 255 bytes of meter data (Request A)\n");
    send_rec(cmd_request_a);

    printf("Now we can request other data\n");
    printf("For example the remaining 255 bytes of meter data (Request B)\n");
    send_rec(cmd_request_b);

    printf("Or request things like the last 6 months of total kwh and reverse kwh\n");
    send_rec(cmd_6mo_totkwh);
    send_rec(cmd_6mo_revkwh);

    printf("When requesting information from the meter (reads)\n");
    printf("you can send as many requests as you like once you sent\n");
    printf("the first connection request (Request A)\n\n\n");

    printf("Now let's try sending a setting to the meter\n");
    printf("Unlike reading information from the meter\n");
    printf("You have to send the connection string and password\n");
    printf("before each setting request\n\n\n");

    printf("Now we will loop 3 times opening and closing the relays\n");
    int x;
    for (x=0; x<3; x++){
        printf("Send Connection Request A\n");
        send_rec(cmd_request_a);

        printf("Send Password\n");
        send_rec(cmd_send_passwd);

        printf("Open Relay A\n");
        send_rec(cmd_relay_open_a);

        sleep(5);

        printf("Send Connection Request A\n");
        send_rec(cmd_request_a);

        printf("Send Password\n");
        send_rec(cmd_send_passwd);

        printf("Close Relay A\n");
        send_rec(cmd_relay_close_a);

        sleep(5);

        printf("Send Connection Request A\n");
        send_rec(cmd_request_a);

        printf("Send Password\n");
        send_rec(cmd_send_passwd);

        printf("Open Relay B\n");
        send_rec(cmd_relay_open_b);

        sleep(5);

        printf("Send Connection Request A\n");
        send_rec(cmd_request_a);

        printf("Send Password\n");
        send_rec(cmd_send_passwd);

        printf("Close Relay B\n");
        send_rec(cmd_relay_close_b);

        sleep(5);
    }
    printf("When you're finished you will close the connection\n");
    send_rec(cmd_close);
    close(sp);
}

// Sending Meter Request Example (version 1.00)

// NodeJS requires serialport, async and sleep modules to connect with meter
// Install these module from shell prompt
// npm install serialport
// npm install async

// Load modules
var serialport = require("serialport"),
    async = require('async')
;

var SerialPort = serialport.SerialPort;


// EKM CRC-16 Table
var CRCTABLE = [
        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
];


// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

function ekm_calc_crc16(buf){
        var crc = 0xffff;
        for (var i = 0, len = buf.length; i < len; i++) {
          var c = buf[i];
          var index = (crc ^ c.charCodeAt( 0 )) & 0xff;
          var crct = CRCTABLE[index];
          crc=(crc>>8)^crct;
        }
        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
        return crc
}


function strToHex(buf) {
    var hex = '';
    for(var i=0;i<buf.length;i++) {
        var pad = ""+buf.charCodeAt(i).toString(16).toUpperCase();
        hex += ' '+ ("00"+pad).slice(-2);
    }
    return hex;
}

// In this code example we make a simple function that will take the command
// you want to send to the meter and make the crc for it before sending.
// After sending we get the response from the meter and check its crc to make sure
// the response is correct
function send_rec(send, callBack){
        // get the the 2nd through the last byte
        // from the send string to create crc
        var crc = ekm_calc_crc16(send.substr(1)) ;
        // Convert CRC from int to byte and add to the send request
        send=send+ String.fromCharCode((crc >> 8) & 0xFF)+String.fromCharCode(crc & 0xFF);

        // Clear response buffer
        response = new Buffer(0);

        // Set Timeout
        var timeout = 5;

        async.series([
         function(callback){
            // Send request
            sp.write(send, function(err, res) {
                if (err) {
                    console.log(err);
                    throw err;
                }
                setTimeout(function() {
                    setImmediate(callback());
                }, timeout*1000);
            });
         },
         function(callback){
            response = response.toString();
            if(response==""){
                console.log("Meter Instruction: ")
                console.log(strToHex(response));
                console.log("Did not trigger a response\n")
            }else{
                // The meter returns HEX 06 if ok
                if(response.substr(0,1)=="\x06"){
                    console.log("OK.\n")
                }                
                // If the meter returns HEX 02 it's the start of a text read
                if(response.substr(0,1)=="\x02"){
                    // Get check CRC in response (byte 2 through the 3rd from the last)
                    // to make sure it's valid
                    crc = ekm_calc_crc16(response.substr(1, response.length-3));
                    // Compare our calculated CRC with the resonse CRC
                    if (String.fromCharCode((crc >> 8) & 0xFF)+String.fromCharCode(crc & 0xFF) ==response.substr(-2)){
                        // Show response
                        console.log(strToHex(response) +"\n");
                    }else{
                        console.log("Meter response CRC failed\n");
                    }
                }    
            }
            setImmediate(callback());
          }
         ],function(error){
             if(error){
                 console.log(error);
                 throw error;
             }
             callBack();
         });

}

// Params for serial port
// Change /dev/ttyUSB0 to match your system
// Open connection to serial port
var sp = new SerialPort("/dev/ttyUSB0", {
        baudRate: 9600,
        parity: 'even',
        dataBits: 7,
        stopBits: 1,
        flowControl: 0,
        bufferSize: 1,
        parser: serialport.parsers.raw
}, false);

// Meter Number
var meter="000300001184";

// Meter Number and Password
// Default password: 00000000
var meter="000300001184";
var password="00000000";

// In this example we have assigned
// some meter request strings to variables

// Get meter data A request and connection request
var cmd_request_a="\x2F\x3F"+meter+"\x30\x30\x21\x0D\x0A";
// Get meter data B request
var cmd_request_b="\x2F\x3F"+meter+"\x30\x31\x21\x0D\x0A";

// Get last 6 months (total kwh)
var cmd_6mo_totkwh="\x01\x52\x31\x02\x30\x30\x31\x31\x03";
// Get last 6 months (reverse kwh)
var cmd_6mo_revkwh="\x01\x52\x31\x02\x30\x30\x31\x32\x03";

// Send Password String
var cmd_send_passwd="\x01\x50\x31\x02\x28"+password+"\x29\x03";

// Relay Open and Close Request Stings
var cmd_relay_open_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x30\x30\x30\x30\x30\x29\x03";
var cmd_relay_open_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x30\x30\x30\x30\x30\x29\x03";
var cmd_relay_close_a="\x01\x57\x31\x02\x30\x30\x38\x31\x28\x31\x30\x30\x30\x30\x29\x03";
var cmd_relay_close_b="\x01\x57\x31\x02\x30\x30\x38\x32\x28\x31\x30\x30\x30\x30\x29\x03";

// Close Connection String
var cmd_close="\x01\x42\x30\x03\x75";

var response = new Buffer(0);

async.series( [
        function(callback){
             sp.open(function(err) {
                console.log("Connection Opened");
                // Get meter response
                sp.on('data', function(read) {
                    if(response.length<255){
                        response = Buffer.concat([response, read]);
                    }
                });
                setImmediate(callback);
            });
        },
        function(callback){
            console.log("So let's give it a try...");
            console.log("First we open the connection to the meter");
            console.log("It will respond with the first 255 bytes of meter data (Request A)");
            send_rec(cmd_request_a, function(){
                setImmediate(callback);
            });
        },
        function(callback){
            console.log("Now we can request other data");
            console.log("For example the remaining 255 bytes of meter data (Request B)");
            send_rec(cmd_request_b, function(){
                setImmediate(callback);
            });
        },
        function(callback){
            console.log("Or request things like the last 6 months of total kwh and reverse kwh");
            send_rec(cmd_6mo_totkwh, function(){
                send_rec(cmd_6mo_revkwh, function(){
                    setImmediate(callback);
                });
            });
        },
        function(callback){
            console.log("When requesting information from the meter (reads)");
            console.log("you can send as many requests as you like once you sent");
            console.log("the first connection request (Request A)\n");

            console.log("Now let's try sending a setting to the meter");
            console.log("Unlike reading information from the meter");
            console.log("You have to send the connection string and password");
            console.log("before each setting request\n");

            console.log("Now we will loop 3 times opening and closing the relays");
            var x = 1;
            async.whilst(
                function () {
                    return (x <=3);
                },
                function (callback) {
                    async.series( [
                        function(callback){
                            console.log("Send Connection Request A");
                            send_rec(cmd_request_a, function(){
                                console.log("Send Password");
                                send_rec(cmd_send_passwd, function(){
                                    console.log("Open Relay A");
                                    send_rec(cmd_relay_open_a, function(){
                                        console.log("Send Connection Request A");
                                        send_rec(cmd_request_a, function(){
                                            console.log("Send Password");
                                            send_rec(cmd_send_passwd, function(){
                                                console.log("Close Relay A");
                                                send_rec(cmd_relay_close_a, function(){
                                                    setImmediate(callback);
                                                });
                                            });
                                        });
                                    });
                                });
                            });
                        },
                        function(callback){
                            console.log("Send Connection Request A");
                            send_rec(cmd_request_a, function(){
                                console.log("Send Password");
                                send_rec(cmd_send_passwd, function(){
                                    console.log("Open Relay B");
                                    send_rec(cmd_relay_open_b, function(){
                                        console.log("Send Connection Request A");
                                        send_rec(cmd_request_a, function(){
                                            console.log("Send Password");
                                            send_rec(cmd_send_passwd, function(){
                                                console.log("Close Relay B");
                                                send_rec(cmd_relay_close_b, function(){
                                                    setImmediate(callback);
                                                });
                                            });
                                        });
                                    });
                                });
                            });
                        }
                ],function (error) {
                        if(error){
                            console.log(error);
                            throw error;
                        }
                        x++;
                        setImmediate(callback);
                });

            },
            function (err) {
                if(err){
                    console.log(err);
                    throw err;
                }
                setImmediate(callback);
            });    
        },
        function(callback){
            console.log("When you're finished you will close the connection");
            send_rec(cmd_close, function(){
                setImmediate(callback);
            });
        }
    ],function (error) {
        if(error){
            console.log(error);
            throw error;
        }
        // Close connection to serial port
        sp.close();
});


Communication Sequence for Reading Meter

If you would like to retrieve, or read, just the meter data then you would pass the following read request string on a v4 meter.

It is not necessary at this point to send a password request string if you are only reading the meter data.

Data A and Connection Request Format:

2F 3F ( 12 Byte Meter Number ) 30 30 21 0D 0A

Once communication with the meter has beed established it is not necessary to continue to send the Meter Data A and Connection Request string.

Example of a read request string for Data A and a meter number of 000300001184:

2F 3F 30 30 30 33 30 30 30 30 31 31 38 34 30 30 21 0D 0A

For example, you can now send the read request string for Meter Data B.

Data B Request Format:

2F 3F ( 12 Byte Meter Number ) 30 31 21 0D 0A

If you look at the request string, the 2 bytes after the meter number, is where the request for Data A or Data B is made. Requesting Data A you will use 30 30. If you want to request Data B you would use 30 31 in your request string to the meter.

At this point you can continue to send requests to the meter to get the Meter Data A or B readings, otherwise you can send the termination string to the meter to terminate the communication connection.

To end communication with the meter pass the following string:

01 42 30 03 75

If more meter readings are needed you can continue to send read requests to the meter as long as the termination string has not been sent.

Lets say that you need specific data returned. You would just pass that specific data request string to the meter.

For example, you need to get the data from your meter for the last 6 months of the Total kWh’s.

Then you would send this request string to the meter:

Total kWh Request String:

01 52 31 02 30 30 31 31 03 ?? ??

Or if you need the meter data for the last 6 months of Reverse kWh:

Reverse kWh Request String:

01 52 31 02 30 30 31 32 03 ?? ??

The two ( 2 ) sets of “Question Marks” ( ?? ?? ) equal the 2 bytes that the CRC-16 Checksum will be.

There is no limit to how many read requests you can send to the meter, as long as you start with the Meter Data A and Connection string first. Once that initial connection has been made with the meter you don’t have to repeat that connection string. You can continue to call the same read request, a different one, or a combination of meter requests.

Remember, once you have finished requesting all the meter reads you want, the close string must be sent. This tells the meter that you are finished and not to keep expecting more data requests to be sent. This also closes the communication connection from your computer to the meter.

Close String

01 42 30 03 75

Communication Sequence for Setting Requests

To send Setting, or write, Requests to the meter, you begin with the same procedure as you would for the reading meter data requests.

You start the communication with the meter by passing the Meter Data A and Connection Request string: 2F 3F ( 12 Byte Meter Number ) 30 30 21 0D 0A.

Once you have made the connection to the meter you will now need to pass the password string along with the Meter Data A and Connection string. The Password string must be included if you want to set, or write, instructions to the meter.

Password String Format:

01 50 31 02 28 ( password ) 29 03 ?? ??

The two ( 2 ) sets of “Question Marks” ( ?? ?? ) equal the 2 bytes that the CRC-16 Checksum will be.

Once the meter has verified that the password is correct it will return a 06, or an O.K., indicating that the password has been accepted.

06 Indicates an OK or that the meter has accepted and verified the password.

Now that the password string has been sent and verified, the meter is ready to accept setting, or write, instructions.

Sending setting instruction to the meter can be anything from setting a new meter number, a new password or setting the time.

For our examples we will show setting strings on how to open and close relays.

To open or close a relay is similar to the way you would send a request string to retrieve Data A from the meter.

Example below is of a request to open Relay A:

01 57 31 02 30 30 38 31 28 31 30 30 30 30 29 03 ?? ??

Lets say you need to close the relay instead of opening it. Then you would pass the following request string to the meter.

Request string to close Relay A:

01 57 31 02 30 30 38 31 28 30 30 30 30 30 29 03 ?? ??

The two ( 2 ) sets of “Question Marks” ( ?? ?? ) equal the 2 bytes that the CRC-16 Checksum will be.

Now that you have sent the setting request to the meter, the meter will send back a 06 indicating that the setting request was successful.

From here there are two ( 2 ) options you can choose from. You can either end the communication with the meter, with the Close String, if this is the only request that was needed to be sent to the meter, or you can continue with another setting request.

If another setting request is needed to be sent to the meter then the Meter Data A and Connection Request string will need to be sent to the meter. Starting the sequence over again.

Complete Setting Request sequence to the meter:

2F 3F ( 12 Byte Meter Number ) 30 30 21 0D 0A Data A and Connection String Request. This is always sent to the meter first to establish the connection.
255 Byte return from the meter. This will be the Meter Data A return
01 50 31 02 28 ( password ) 29 03 ?? ?? Password String
06 This is what the meter will return once your password is verified.
01 57 31 02 30 30 38 31 28 31 30 30 30 30 29 03 ?? ?? This is the write command string that is sent to the meter. Example here is the request to open Relay A.
06 This is what the meter will return indicating that the setting was successful.
2F 3F ( 12 Byte Meter Number ) 30 30 21 0D 0A Data A and Connection String Request. This is always sent to the meter first to establish the connection.
255 Byte return from the meter. This will be the Meter Data A return
01 50 31 02 28 ( password ) 29 03 ?? ?? Password String
06 This is what the meter will return once your password is verified.
01 57 31 02 30 30 38 32 28 30 30 30 30 30 29 03 ?? ?? This is the write command string that is sent to the meter. Example here is the request to close Relay B.
06 This is what the meter will return indicating that the setting was successful.
01 42 30 03 75 Close String to terminate the connection with the meter.

RS-485 Parsing Responses

Parsing Responses from meter


# Parsing Meter Request Example (version 1.00)

# Requires the SerialPort gem
# (http://rubygems.org/gems/serialport)
# gem install serialport

require "serialport"
require 'json'
require 'pp'



# EKM CRC-16 Table
CRCTABLE = [
        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
];


# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");
def ekm_calc_crc16(buf)
        crc = 0xffff
        i = 0
        while i < buf.length  do
            c = buf[i].chr
            index = (crc ^ c.ord) & 0xff
            crct = CRCTABLE[index]
            crc=(crc>>8)^crct
            i +=1
        end

        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
        return crc
end


# In this code example we make a simple function that will take the command
# you want to send to the meter and make the crc for it before sending.
# After sending we get the response from the meter and check its crc to make sure
# the response is correct
def send_rec(send)
        # get the the 2nd through the last byte
        # from the send string to create crc
        crc = ekm_calc_crc16(send[1..-1])

        # Convert CRC from int to byte and add to the send request
        send=send+((crc >> 8) & 0xFF).chr + (crc & 0xFF).chr

        # Send request
        $sp.write(send)

        #Set Timeout
        timeout = 5

        # Get meter response
        now=Time.now
        read=""
        response=""
        count=0

        while count<255
            read=$sp.getc
            if read
                 now=Time.now
                 count+=1
                 response.concat(read)
            end
            if Time.now > now + timeout
                 break
            end
        end

        if response==""
            print("Meter Instruction: \n")
            print(send.unpack('C*').map {|e| "%02X" % e}.join(" "))
            print("\nDid not trigger a response\n\n")
            return
        end

        # The meter returns HEX 06 if ok
        if(response[0]=="\x06")
            print("OK.\n\n")
        end

    # If the meter returns HEX 02 it's the start of a text read
        if(response[0]=="\x02")
            # Get check CRC in response (byte 2 through the 3rd from the last)
            # to make sure it's valid
            crc =  ekm_calc_crc16(response[1..-3])

            # Compare our calculated CRC with the resonse CRC
            if (((crc >> 8) & 0xFF).chr + (crc & 0xFF).chr ==response[-2..-1])
                # Return response
                return(response)
            else
                print("Meter response CRC failed\n\n")
            end
      end
      return

end

# This function takes the response from the meter
# and parses its output into an array using the defined table
def parse_response(response, table)
    table_array=JSON.parse(table)
    response_array={};
    pointer=0;
    table_array.each do |obj|
        key = obj[0]
        val = obj[1]
        ln = pointer+val-1
        response_array[key]= response[pointer..ln];
        pointer+=val;
    end
    return(response_array);
end


# Params for serial port
# Change /dev/ttyUSB0 to match your system
port_str = "/dev/ttyUSB0"
baud_rate = 9600
data_bits = 7
stop_bits = 1
parity = SerialPort::EVEN
flow = SerialPort::NONE

# Open connection to serial port
$sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits, parity)
$sp.flow_control = flow

# Meter Number and Password
meter="000300001184"

# In this example we have assigned
# some meter request strings to variables

# Get meter data A request and connection request
cmd_request_a="\x2F\x3F"+meter+"\x30\x30\x21\x0D\x0A"
# Get meter data B request
cmd_request_b="\x2F\x3F"+meter+"\x30\x31\x21\x0D\x0A"

# Close Connection String
cmd_close="\x01\x42\x30\x03\x75"

# Now we set up parsing tables for the data A and B requests
tbl_request_a='{
"reserved_start_byte":1,
"Model":2,
"Firmware":1,
"Meter_Address":12,
"kWh_Tot":8,
"Reactive_Energy_Tot":8,
"Rev_kWh_Tot":8,
"kWh_Ln_1":8,
"kWh_Ln_2":8,
"kWh_Ln_3":8,
"Rev_kWh_Ln_1":8,
"Rev_kWh_Ln_2":8,
"Rev_kWh_Ln_3":8,
"Resettable_kWh_Tot":8,
"Resettable_Rev_kWh_Tot":8,
"RMS_Volts_Ln_1":4,
"RMS_Volts_Ln_2":4,
"RMS_Volts_Ln_3":4,
"Amps_Ln_1":5,
"Amps_Ln_2":5,
"Amps_Ln_3":5,
"RMS_Watts_Ln_1":7,
"RMS_Watts_Ln_2":7,
"RMS_Watts_Ln_3":7,
"RMS_Watts_Tot":7,
"Power_Factor_Ln_1":4,
"Power_Factor_Ln_2":4,
"Power_Factor_Ln_3":4,
"Reactive_Pwr_Ln_1":7,
"Reactive_Pwr_Ln_2":7,
"Reactive_Pwr_Ln_3":7,
"Reactive_Pwr_Tot":7,
"Line_Freq":4,
"Pulse_Cnt_1":8,
"Pulse_Cnt_2":8,
"Pulse_Cnt_3":8,
"State_Inputs":1,
"State_Watts_Dir":1,
"State_Out":1,
"kWh_Scale":1,
"reserved_end_data":2,
"Meter_Time":14,
"reserved_type":2,
"reserved_end":4,
"crc16":2}';

tbl_request_b='{
"reserved_start_byte":1,
"Model":2,
"Firmware":1,
"Meter_Address":12,
"kWh_Tariff_1":8,
"kWh_Tariff_2":8,
"kWh_Tariff_3":8,
"kWh_Tariff_4":8,
"Rev_kWh_Tariff_1":8,
"Rev_kWh_Tariff_2":8,
"Rev_kWh_Tariff_3":8,
"Rev_kWh_Tariff_4":8,
"RMS_Volts_Ln_1":4,
"RMS_Volts_Ln_2":4,
"RMS_Volts_Ln_3":4,
"Amps_Ln_1":5,
"Amps_Ln_2":5,
"Amps_Ln_3":5,
"RMS_Watts_Ln_1":7,
"RMS_Watts_Ln_2":7,
"RMS_Watts_Ln_3":7,
"RMS_Watts_Tot":7,
"Power_Factor_Adj_Ln_1":4,
"Power_Factor_Adj_Ln_2":4,
"Power_Factor_Adj_Ln_3":4,
"RMS_Watts_Max_Demand":8,
"Max_Demand_Period":1,
"Pulse_Ratio_1":4,
"Pulse_Ratio_2":4,
"Pulse_Ratio_3":4,
"CT_Ratio":4,
"reserved_b2":1,
"Pulse_Output_Ratio":4,
"reserved_b3":56,
"Meter_Time":14,
"reserved_type":2,
"reserved_end":4,
"crc16":2}';


print("So let's give it a try...\n")
print("First we open the connection to the meter\n")
print("It will respond with the first 255 bytes of meter data (Request A)\n")
request_a=parse_response(send_rec(cmd_request_a),tbl_request_a);
pp request_a

print("Now we can request other data\n")
print("For example the remaining 255 bytes of meter data (Request B)\n")
request_b=parse_response(send_rec(cmd_request_b),tbl_request_b);
pp request_b

print("When you're finished you will close the connection\n")
send_rec(cmd_close)

# Close connection to serial port
$sp.close

# Parsing Meter Request Example (version 1.00)

# Requires pyserial module
# pip3 install pyserial

import serial
import simplejson as json
import pprint

# EKM CRC-16 Table
CRCTABLE = [
        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
];

# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

def ekm_calc_crc16(buf):
        crc = 0xffff
        for c in buf:
                index = (crc ^ ord( c )) & 0xff
                crct = CRCTABLE[index]
                crc=(crc>>8)^crct
        crc = (crc << 8) | (crc >> 8)
        crc &= 0x7F7F
        return crc


# In this code example we make a simple function that will take the command
# you want to send to the meter and make the crc for it before sending.
# After sending we get the response from the meter and check its crc to make sure
# the response is correct

def send_rec(send):
        # get the the 2nd through the last byte
        # from the send string to create crc
        crc = ekm_calc_crc16(send[1:])

        # Convert CRC from int to byte and add to the send request
        send=send+chr((crc >> 8) & 0xFF) + chr(crc & 0xFF)
        # Send request
        sp.write(send.encode())
        # Get meter response
        response = sp.read(255)
        response=str(response,'ascii')

        if response=="":
                print("Meter Instruction: ")
                print(" ".join("{0:02x}".format(ord( c )) for c in send))
                print("Did not trigger a response\n\n")
                return

        # The meter returns HEX 06 if ok
        if response[0]=="\x06":
                print("OK.\n\n")
        # If the meter returns HEX 02 it's the start of a text read
        if response[0]=="\x02":
                # Get check CRC in response (byte 2 through the 3rd from the last)
                # to make sure it's valid
                crc =  ekm_calc_crc16(response[1:-2])

                # Compare our calculated CRC with the resonse CRC
                if chr((crc >> 8) & 0xFF) + chr(crc & 0xFF)==response[-2:]:
                        # Return response
                        return(response)
                else:
                        print("Meter response CRC failed\n\n")


# This function takes the response from the meter
# and parses its output into an array using the defined table
def parse_response(response,table):
     table_array= json.loads(table,  object_pairs_hook=json.OrderedDict)
     response_array={}
     pointer = 0
     for key in table_array:
        val = table_array[key]
        end = val+pointer
        response_array[key]=response[pointer:end]
        pointer+=val;
     return(response_array);

# Open connection to serial port
# Change /dev/ttyUSB0 to match your system
sp = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=9600,
    parity=serial.PARITY_EVEN,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.SEVENBITS,
    xonxoff=0,
    timeout=5
)

# Meter Number and Password
meter="000300001184"


# In this example we have assigned
# some meter request strings to variables

# Get meter data A request and connection request
cmd_request_a="\x2F\x3F"+meter+"\x30\x30\x21\x0D\x0A"
# Get meter data B request
cmd_request_b="\x2F\x3F"+meter+"\x30\x31\x21\x0D\x0A"

# Close Connection String
cmd_close="\x01\x42\x30\x03\x75"

# Now we set up parsing tables for the data A and B requests
tbl_request_a='{ "reserved_start_byte":1, "Model":2, "Firmware":1, "Meter_Address":12, "kWh_Tot":8, "Reactive_Energy_Tot":8, "Rev_kWh_Tot":8, "kWh_Ln_1":8, "kWh_Ln_2":8, "kWh_Ln_3":8, "Rev_kWh_Ln_1":8, "Rev_kWh_Ln_2":8, "Rev_kWh_Ln_3":8, "Resettable_kWh_Tot":8, "Resettable_Rev_kWh_Tot":8, "RMS_Volts_Ln_1":4, "RMS_Volts_Ln_2":4, "RMS_Volts_Ln_3":4, "Amps_Ln_1":5, "Amps_Ln_2":5, "Amps_Ln_3":5, "RMS_Watts_Ln_1":7, "RMS_Watts_Ln_2":7, "RMS_Watts_Ln_3":7, "RMS_Watts_Tot":7, "Power_Factor_Ln_1":4, "Power_Factor_Ln_2":4, "Power_Factor_Ln_3":4, "Reactive_Pwr_Ln_1":7, "Reactive_Pwr_Ln_2":7, "Reactive_Pwr_Ln_3":7, "Reactive_Pwr_Tot":7, "Line_Freq":4, "Pulse_Cnt_1":8, "Pulse_Cnt_2":8, "Pulse_Cnt_3":8, "State_Inputs":1, "State_Watts_Dir":1, "State_Out":1, "kWh_Scale":1, "reserved_end_data":2, "Meter_Time":14, "reserved_type":2, "reserved_end":4, "crc16":2}'

tbl_request_b='{ "reserved_start_byte":1, "Model":2, "Firmware":1, "Meter_Address":12, "kWh_Tariff_1":8, "kWh_Tariff_2":8, "kWh_Tariff_3":8, "kWh_Tariff_4":8, "Rev_kWh_Tariff_1":8, "Rev_kWh_Tariff_2":8, "Rev_kWh_Tariff_3":8, "Rev_kWh_Tariff_4":8, "RMS_Volts_Ln_1":4, "RMS_Volts_Ln_2":4, "RMS_Volts_Ln_3":4, "Amps_Ln_1":5, "Amps_Ln_2":5, "Amps_Ln_3":5, "RMS_Watts_Ln_1":7, "RMS_Watts_Ln_2":7, "RMS_Watts_Ln_3":7, "RMS_Watts_Tot":7, "Power_Factor_Adj_Ln_1":4, "Power_Factor_Adj_Ln_2":4, "Power_Factor_Adj_Ln_3":4, "RMS_Watts_Max_Demand":8, "Max_Demand_Period":1, "Pulse_Ratio_1":4, "Pulse_Ratio_2":4, "Pulse_Ratio_3":4, "CT_Ratio":4, "reserved_b2":1, "Pulse_Output_Ratio":4, "reserved_b3":56, "Meter_Time":14, "reserved_type":2, "reserved_end":4, "crc16":2}'

print("So let's give it a try...")
print("First we open the connection to the meter")
print("It will respond with the first 255 bytes of meter data (Request A)")
request_a=parse_response(send_rec(cmd_request_a),tbl_request_a)
pprint.pprint(request_a)

print("Now we can request other data")
print("For example the remaining 255 bytes of meter data (Request B)")
request_b=parse_response(send_rec(cmd_request_b),tbl_request_b)
pprint.pprint(request_b)

print("When you're finished you will close the connection")
send_rec(cmd_close)

# Close connection to serial port
sp.close

<?php
// Parsing Meter Request Example (version 1.00)

// Requires the pecl_dio extenion
// On CentOS
// yum install php-pear
// pecl install dio

// EKM CRC-16 Table
$CRCTABLE = array(
        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
);

// Open connection to serial port
// Change /dev/ttyUSB0 to match your system
$sp = dio_open( '/dev/ttyUSB0', O_RDWR | O_NONBLOCK );

// Configure the port
dio_tcsetattr( $sp, array(
   'baud'         => 9600,
   'bits'         => 7,
   'stop'         => 1,
   'parity'       => 2,
   'flow_control' => 0,
   'is_canonical' => 0
) );


// Meter Number and Password
$meter="000300001184";

// In this example we have assigned
// some meter request strings to variables

// Get meter data A request and connection request
$cmd_request_a="\x2F\x3F".$meter."\x30\x30\x21\x0D\x0A";
// Get meter data B request
$cmd_request_b="\x2F\x3F".$meter."\x30\x31\x21\x0D\x0A";

// Close Connection String
$cmd_close="\x01\x42\x30\x03\x75";

// Now we set up parsing tables for the data A and B requests
$tbl_request_a='{
"reserved_start_byte":1,
"Model":2,
"Firmware":1,
"Meter_Address":12,
"kWh_Tot":8,
"Reactive_Energy_Tot":8,
"Rev_kWh_Tot":8,
"kWh_Ln_1":8,
"kWh_Ln_2":8,
"kWh_Ln_3":8,
"Rev_kWh_Ln_1":8,
"Rev_kWh_Ln_2":8,
"Rev_kWh_Ln_3":8,
"Resettable_kWh_Tot":8,
"Resettable_Rev_kWh_Tot":8,
"RMS_Volts_Ln_1":4,
"RMS_Volts_Ln_2":4,
"RMS_Volts_Ln_3":4,
"Amps_Ln_1":5,
"Amps_Ln_2":5,
"Amps_Ln_3":5,
"RMS_Watts_Ln_1":7,
"RMS_Watts_Ln_2":7,
"RMS_Watts_Ln_3":7,
"RMS_Watts_Tot":7,
"Power_Factor_Ln_1":4,
"Power_Factor_Ln_2":4,
"Power_Factor_Ln_3":4,
"Reactive_Pwr_Ln_1":7,
"Reactive_Pwr_Ln_2":7,
"Reactive_Pwr_Ln_3":7,
"Reactive_Pwr_Tot":7,
"Line_Freq":4,
"Pulse_Cnt_1":8,
"Pulse_Cnt_2":8,
"Pulse_Cnt_3":8,
"State_Inputs":1,
"State_Watts_Dir":1,
"State_Out":1,
"kWh_Scale":1,
"reserved_end_data":2,
"Meter_Time":14,
"reserved_type":2,
"reserved_end":4,
"crc16":2}';

$tbl_request_b='{
"reserved_start_byte":1,
"Model":2,
"Firmware":1,
"Meter_Address":12,
"kWh_Tariff_1":8,
"kWh_Tariff_2":8,
"kWh_Tariff_3":8,
"kWh_Tariff_4":8,
"Rev_kWh_Tariff_1":8,
"Rev_kWh_Tariff_2":8,
"Rev_kWh_Tariff_3":8,
"Rev_kWh_Tariff_4":8,
"RMS_Volts_Ln_1":4,
"RMS_Volts_Ln_2":4,
"RMS_Volts_Ln_3":4,
"Amps_Ln_1":5,
"Amps_Ln_2":5,
"Amps_Ln_3":5,
"RMS_Watts_Ln_1":7,
"RMS_Watts_Ln_2":7,
"RMS_Watts_Ln_3":7,
"RMS_Watts_Tot":7,
"Power_Factor_Adj_Ln_1":4,
"Power_Factor_Adj_Ln_2":4,
"Power_Factor_Adj_Ln_3":4,
"RMS_Watts_Max_Demand":8,
"Max_Demand_Period":1,
"Pulse_Ratio_1":4,
"Pulse_Ratio_2":4,
"Pulse_Ratio_3":4,
"CT_Ratio":4,
"reserved_b2":1,
"Pulse_Output_Ratio":4,
"reserved_b3":56,
"Meter_Time":14,
"reserved_type":2,
"reserved_end":4,
"crc16":2}';

echo "So let's give it a try...\n";
echo "First we open the connection to the meter\n";
echo "It will respond with the first 255 bytes of meter data (Request A)\n";
$request_a=parse_response(send_rec($cmd_request_a),$tbl_request_a);
var_dump($request_a);


echo "Now we can request other data\n";
echo "For example the remaining 255 bytes of meter data (Request B)\n";
$request_b=parse_response(send_rec($cmd_request_b),$tbl_request_b);
var_dump($request_b);

echo "When you're finished you will close the connection\n";
send_rec($cmd_close);

// Close connection to serial port
dio_close( $sp );


// In this code example we make a simple function that will take the command
// you want to send to the meter and make the crc for it before sending.
// After sending we get the response from the meter and check its crc to make sure
// the response is correct

function send_rec($send){
        global $sp;

        // get the the 2nd through the last byte
        // from the send string to create crc
        $crc = ekm_calc_crc16(substr($send,1));

        // Convert CRC from int to byte and add to the send request
        $send=$send.chr(($crc >> 8) & 0xFF).chr($crc & 0xFF);

        // Send request
        dio_write($sp,$send);

        // Set Timeout
        $timeout = 5;

        // Get meter response        
        $read = '';
        $response = '';
        $now=time();
        while( strlen($response)<255 && time()<$now+$timeout) {
                $read = dio_read($sp,1);
                $response .= $read;
        }

        if ($response==""){                
                echo "Meter Instruction: \n";
                echo strToHex($send)."\n";
                echo "Did not trigger a response\n\n";
                return;
        }

        // The meter returns HEX 06 if ok
        if (substr($response,0,1)=="\x06")
                echo "OK.\n\n";
        // If the meter returns HEX 02 it's the start of a text read
        if (substr($response,0,1)=="\x02"){
                // Get check CRC in response (byte 2 through the 3rd from the last)
                // to make sure it's valid
                $crc =  ekm_calc_crc16(substr($response,1,-2));
                // Compare our calculated CRC with the resonse CRC
                if (chr(($crc >> 8) & 0xFF).chr($crc & 0xFF)==substr($response,-2))
                        // Return response
                        return($response);
                else
                        echo "Meter response CRC failed\n\n";
        }
}


// This function takes the response from the meter
// and parses its output into an array using the defined table

function parse_response($response,$table){

    $table_array=json_decode($table);
    $response_array=array();        
    $pointer=0;

    foreach ($table_array as $key => $val){
                $response_array[$key]=substr($response,$pointer,$val);
                $pointer+=$val;
    }

    return($response_array);
}

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

function ekm_calc_crc16($buf){
        $crc = 0xffff;
        global $CRCTABLE;
        $len = strlen($buf);
        for($i=0; $i< $len; $i++){
                $c = $buf[$i];
                $index = ($crc ^ ord($c)) & 0xff;
                $crct = $CRCTABLE[$index];
                $crc=($crc>>8) ^ $crct;
        }
        $crc = ($crc << 8) | ($crc >> 8);
        $crc &= 0x7F7F;
        return $crc;
}



function strToHex($buf)
{
    $hex='';
    for ($i=0; $i < strlen($buf); $i++)
    {
        $hex .= sprintf("%02X",ord($buf[$i])).' ';
    }
    return $hex;
}

?>

# Parsing Meter Request Example (version 1.00)

# Requires the Device::SerialPort module
# On CentOS
# yum install perl-Device-SerialPort
use Device::SerialPort;
use Data::Dumper;


# EKM CRC-16 Table
use constant CRCTABLE => [
        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
];


# This function makes use of the CRC table to calulate the CRC
# example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

sub ekm_calc_crc16 {
     my $buf = shift(@_);
     my $crc=0xffff;
     my $s;

        my @arr = split("", $buf);
        for(my $i=0; $i<length($buf); $i++) {
          my $chr =  ord($arr[$i]);
          $s = ($crc ^ $chr) & 0xff;
          my $crct = CRCTABLE->[$s];
          $crc = ($crc>>8) ^ $crct;
        }
     $crc = ($crc << 8) | ($crc >> 8);
     $crc &= 0x7F7F;

     return $crc;
}


# In this code example we make a simple function that will take the command
# you want to send to the meter and make the crc for it before sending.
# After sending we get the response from the meter and check its crc to make sure
# the response is correct
sub send_rec{
    my $send = shift(@_);
    # get the the 2nd through the last byte
    # from the send string to create crc
    my $crc = ekm_calc_crc16(substr($send, 1)) ;

    # Convert CRC from int to byte and add to the send request
    $send=$send.chr(($crc >> 8) & 0xFF).chr($crc & 0xFF);

    # Send request
    $sp->write($send);

    #Set Timeout
    my $timeout = 5;

    # Get meter response
    my $count=0;
    my $response="";
    while ($timeout>0 && $count<255) {
        my ($chars,$read)=$sp->read(255);
            if ($chars > 0) {
                $count+=$chars;
                $response.=$read;
            }
            else {
                $timeout--;
            }
        }


    if(length($response)==0){
        print("Meter Instruction\n" );
        # Show response as HEX
        $send =~ s/(.)/sprintf("%02X",ord($1))." "/seg;
        print $send;
        print("\nDid not trigger a response\n\n");
        return;
    }


    # The meter returns HEX 06 if ok
    if(substr($response,0,1) eq "\x06"){
        print("OK.\n\n");
    }    

    # If the meter returns HEX 02 it's the start of a text read
    if(substr($response,0,1) eq "\x02" && length($response) ==255){
        # Get check CRC in response (byte 2 through the 3rd from the last)
        # to make sure it's valid
        $crc = ekm_calc_crc16(substr($response,1,-2));

        # Compare our calculated CRC with the resonse CRC
        if ((chr(($crc >> 8) & 0xFF).chr($crc & 0xFF)) ==substr($response,-2)){
            # Return response
            return($response);
        }    
        else{
            print "Meter response CRC failed\n\n";
        }    

    }    

}

# This function takes the response from the meter
# and parses its output into an array using the defined table

sub parse_response{
    my ($response,$table) = (@_);
    my @table_array = split(',',$table);
    my %response_array;
    my $pointer=0;
    for($index=0;$index<=$#table_array;$index++){
       my ($key,$val) = split(":",$table_array[$index]);
       $key =~ tr/"\r\n//d;
       $response_array{$key} = substr($response,$pointer,$val);
       $pointer+=$val;
    }
    return(%response_array);
}


# Params for serial port
# Change /dev/ttyUSB0 to match your system
# Open connection to serial port
$sp = new Device::SerialPort("/dev/ttyUSB0");
# Configure the port
$sp->user_msg(ON);
$sp->baudrate(9600);
$sp->parity("even");
$sp->databits(7);
$sp->stopbits(1);
$sp->handshake("xoff");
$sp->write_settings;
# don't wait for each character
$sp->read_char_time(0);     
# wait for 1 second per unfulfilled read
$sp->read_const_time(1000);


# Meter Number and Password
my $meter="000300001184";

# In this example we have assigned
# some meter request strings to variables

# Get meter data A request and connection request
my $cmd_request_a="\x2F\x3F$meter\x30\x30\x21\x0D\x0A";
# Get meter data B request
my $cmd_request_b="\x2F\x3F$meter\x30\x31\x21\x0D\x0A";

# Close Connection String
my $cmd_close="\x01\x42\x30\x03\x75";

# Now we set up parsing tables for the data A and B requests
my $tbl_request_a='{
"reserved_start_byte":1,
"Model":2,
"Firmware":1,
"Meter_Address":12,
"kWh_Tot":8,
"Reactive_Energy_Tot":8,
"Rev_kWh_Tot":8,
"kWh_Ln_1":8,
"kWh_Ln_2":8,
"kWh_Ln_3":8,
"Rev_kWh_Ln_1":8,
"Rev_kWh_Ln_2":8,
"Rev_kWh_Ln_3":8,
"Resettable_kWh_Tot":8,
"Resettable_Rev_kWh_Tot":8,
"RMS_Volts_Ln_1":4,
"RMS_Volts_Ln_2":4,
"RMS_Volts_Ln_3":4,
"Amps_Ln_1":5,
"Amps_Ln_2":5,
"Amps_Ln_3":5,
"RMS_Watts_Ln_1":7,
"RMS_Watts_Ln_2":7,
"RMS_Watts_Ln_3":7,
"RMS_Watts_Tot":7,
"Power_Factor_Ln_1":4,
"Power_Factor_Ln_2":4,
"Power_Factor_Ln_3":4,
"Reactive_Pwr_Ln_1":7,
"Reactive_Pwr_Ln_2":7,
"Reactive_Pwr_Ln_3":7,
"Reactive_Pwr_Tot":7,
"Line_Freq":4,
"Pulse_Cnt_1":8,
"Pulse_Cnt_2":8,
"Pulse_Cnt_3":8,
"State_Inputs":1,
"State_Watts_Dir":1,
"State_Out":1,
"kWh_Scale":1,
"reserved_end_data":2,
"Meter_Time":14,
"reserved_type":2,
"reserved_end":4,
"crc16":2}';

my $tbl_request_b='{
"reserved_start_byte":1,
"Model":2,
"Firmware":1,
"Meter_Address":12,
"kWh_Tariff_1":8,
"kWh_Tariff_2":8,
"kWh_Tariff_3":8,
"kWh_Tariff_4":8,
"Rev_kWh_Tariff_1":8,
"Rev_kWh_Tariff_2":8,
"Rev_kWh_Tariff_3":8,
"Rev_kWh_Tariff_4":8,
"RMS_Volts_Ln_1":4,
"RMS_Volts_Ln_2":4,
"RMS_Volts_Ln_3":4,
"Amps_Ln_1":5,
"Amps_Ln_2":5,
"Amps_Ln_3":5,
"RMS_Watts_Ln_1":7,
"RMS_Watts_Ln_2":7,
"RMS_Watts_Ln_3":7,
"RMS_Watts_Tot":7,
"Power_Factor_Adj_Ln_1":4,
"Power_Factor_Adj_Ln_2":4,
"Power_Factor_Adj_Ln_3":4,
"RMS_Watts_Max_Demand":8,
"Max_Demand_Period":1,
"Pulse_Ratio_1":4,
"Pulse_Ratio_2":4,
"Pulse_Ratio_3":4,
"CT_Ratio":4,
"reserved_b2":1,
"Pulse_Output_Ratio":4,
"reserved_b3":56,
"Meter_Time":14,
"reserved_type":2,
"reserved_end":4,
"crc16":2}';


print("So let's give it a try...\n");
print("First we open the connection to the meter\n");
print("It will respond with the first 255 bytes of meter data (Request A)\n");
%request_a=parse_response(send_rec($cmd_request_a),$tbl_request_a);
print Dumper(\%request_a);

print("Now we can request other data\n");
print("For example the remaining 255 bytes of meter data (Request B)\n");
%request_b=parse_response(send_rec($cmd_request_b),$tbl_request_b);
print Dumper(\%request_b);

print("When you're finished you will close the connection\n");
send_rec($cmd_close);

# Close connection to serial port
$sp->close || die "failed to close";
undef $sp;

/*
 Parsing Meter Request Example (version 1.00)

 Download the jssc.jar (java-simple-serial-connector)
 from: https://code.google.com/archive/p/java-simple-serial-connector/

 Download the correct org.json jar version for your
 needs from: http://mvnrepository.com/artifact/org.json/json
 This example uses version 20131018 accessible here:
 http://mvnrepository.com/artifact/org.json/json/20131018

 Instructions to run this program

 1. Put this code in a file named connect.java
 2. Copy the downloaded jssc.jar and connect.java to the same directory
 3. Compile
  javac -cp .:./jssc.jar:./json-20131018.jar ./connect.java
 4. Run
  java -cp .:./jssc.jar:./json-20131018.jar connect
*/

// Import required classes
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.*;

import jssc.*;
import org.json.*;

public class connect {

static SerialPort serialPort;
// EKM CRC-16 Table
private static final int[] CRCTABLE = {
    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 };

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");


public static int ekm_calc_crc16(String buf) {
    int crc = 0xffff;
    if (buf != null) {

        char[] charArr = buf.toCharArray();
        for (int i = 0, len = charArr.length; i < len; i++) {
            int c = charArr[i];
            int index = (crc ^ c) & 0xff;
            int crct = CRCTABLE[index];
            crc = ((crc >> 8) ^ crct);
        }
        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
    }

    return crc;

}

// This function takes the response from the meter
// and parses its output into an array using the defined table

public static JSONObject parse_response(String response,String table){
    //remove curly brackets
    table = table.substring(1, table.length()-1);

    //split the string to creat key-value pairs
    String[] keyValuePairs = table.split(",");             
    Map<String,Integer> map = new LinkedHashMap<String,Integer>();               

    //iterate over the pairs
    for(String pair : keyValuePairs)                        
    {
        String[] entry = pair.split(":");                   
        map.put(entry[0].trim(), new Integer(entry[1].trim()));
    }

    JSONObject response_array = new JSONObject();
    int pointer=0;

    for (String key : map.keySet()) {
        Integer val = map.get(key);
        response_array.put(key, response.substring(pointer, pointer+val));
        pointer+=val;
    }

    return response_array;
}


// In this code example we make a simple function that will take the command
// you want to send to the meter and make the crc for it before sending.
// After sending we get the response from the meter and check its crc to make sure
// the response is correct

public static String send_rec(String send) throws SerialPortException{
    String firstByte;
    String response;
    byte[] buffer = "\r\n".getBytes();

    // get the the 2nd through the last byte
    // from the send string to create crc
    int crc = ekm_calc_crc16(send.substring(1));
    send=send+(char)((crc >> 8) & 0xFF)+(char)(crc & 0xFF);

    // Send request
    serialPort.writeBytes(send.getBytes());

    // Set connection timeout;
    int timeout=5;

    // Get byte 1 of meter response
    try {
    firstByte = serialPort.readString(1,timeout*1000);
    } catch (SerialPortTimeoutException e1)
    {
            System.out.println("Meter Instruction: ");
            System.out.println(byteArrayToHex(send)+"");
            System.out.println("Did not trigger a response\n");
            return "";
    }

    // The meter returns HEX 06 if ok
    if ((char)firstByte.charAt(0)==(char)(6 & 0xFF)){
        System.out.println("OK.\n");
        // Clear response buffer
        serialPort.purgePort(SerialPort.PURGE_RXCLEAR | SerialPort.PURGE_TXCLEAR);
        return "";
    }

    // Get rest of meter response
    try {
    buffer  = serialPort.readBytes(254,timeout*1000);
    } catch (SerialPortTimeoutException e1)
    {
            System.out.println("Did receive complete response\n");
            // Clear response buffer
            serialPort.purgePort(SerialPort.PURGE_RXCLEAR | SerialPort.PURGE_TXCLEAR);
            return "";
    }
    response = new String(buffer);

    // Clear response buffer
    serialPort.purgePort(SerialPort.PURGE_RXCLEAR | SerialPort.PURGE_TXCLEAR);

    // If the meter returns HEX 02 it's the start of a text read
    if ((char)firstByte.charAt(0) == (char)(2 & 0xFF)){

            // Put 02 back in the response beings it
            // was pulled out when testing the first byte
            response=""+(char)(2)+response;

            // Get check CRC in response (byte 2 through the 3rd from the last)
            // to make sure it's valid
            crc =  ekm_calc_crc16(response.substring(1, 253));
            // Compare our calculated CRC with the resonse CRC
            if ((char)((crc >> 8) & 0xFF)==(char)response.charAt(253) && (char)(crc & 0xFF)==(char)response.charAt(254)){
                // Return response
                return response;
            }
            else{
                System.out.println( "Meter response CRC failed\n");
                return "";
            }
    }
    return "";
}

public static void main(String[] args) {
    serialPort = new SerialPort("/dev/ttyUSB0");
    try {
        // Open connection to serial port
        // Change /dev/ttyUSB0 to match your system

        serialPort.openPort();
        // Configure the port
        serialPort.setParams(SerialPort.BAUDRATE_9600,
                             SerialPort.DATABITS_7,
                             SerialPort.STOPBITS_1,
                             SerialPort.PARITY_EVEN);
        serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);


        // Meter Number and Password
        // Default password: 00000000
        String meter="000300001184";

        // In this example we have assigned
        // some meter request strings to variables

        // Get meter data A request and connection request
        String cmd_request_a="\u002F\u003F"+meter+"\u0030\u0030\u0021\r\n";
        // Get meter data B request
        String cmd_request_b="\u002F\u003F"+meter+"\u0030\u0031\u0021\r\n";


        // Close Connection String
        String cmd_close="\u0001\u0042\u0030\u0003\u0075";

        // Now we set up parsing tables for the data A and B requests
        String tbl_request_a="{ reserved_start_byte:1, Model:2, Firmware:1, Meter_Address:12, kWh_Tot:8, Reactive_Energy_Tot:8, Rev_kWh_Tot:8, kWh_Ln_1:8, kWh_Ln_2:8, kWh_Ln_3:8, Rev_kWh_Ln_1:8, Rev_kWh_Ln_2:8, Rev_kWh_Ln_3:8, Resettable_kWh_Tot:8, Resettable_Rev_kWh_Tot:8, RMS_Volts_Ln_1:4, RMS_Volts_Ln_2:4, RMS_Volts_Ln_3:4, Amps_Ln_1:5, Amps_Ln_2:5, Amps_Ln_3:5, RMS_Watts_Ln_1:7, RMS_Watts_Ln_2:7, RMS_Watts_Ln_3:7, RMS_Watts_Tot:7, Power_Factor_Ln_1:4, Power_Factor_Ln_2:4, Power_Factor_Ln_3:4, Reactive_Pwr_Ln_1:7, Reactive_Pwr_Ln_2:7, Reactive_Pwr_Ln_3:7, Reactive_Pwr_Tot:7, Line_Freq:4, Pulse_Cnt_1:8, Pulse_Cnt_2:8, Pulse_Cnt_3:8, State_Inputs:1, State_Watts_Dir:1, State_Out:1, kWh_Scale:1, reserved_end_data:2, Meter_Time:14, reserved_type:2, reserved_end:4, crc16:2}";

        String tbl_request_b="{ reserved_start_byte:1, Model:2, Firmware:1, Meter_Address:12, kWh_Tariff_1:8, kWh_Tariff_2:8, kWh_Tariff_3:8, kWh_Tariff_4:8, Rev_kWh_Tariff_1:8, Rev_kWh_Tariff_2:8, Rev_kWh_Tariff_3:8, Rev_kWh_Tariff_4:8, RMS_Volts_Ln_1:4, RMS_Volts_Ln_2:4, RMS_Volts_Ln_3:4, Amps_Ln_1:5, Amps_Ln_2:5, Amps_Ln_3:5, RMS_Watts_Ln_1:7, RMS_Watts_Ln_2:7, RMS_Watts_Ln_3:7, RMS_Watts_Tot:7, Power_Factor_Adj_Ln_1:4, Power_Factor_Adj_Ln_2:4, Power_Factor_Adj_Ln_3:4, RMS_Watts_Max_Demand:8, Max_Demand_Period:1, Pulse_Ratio_1:4, Pulse_Ratio_2:4, Pulse_Ratio_3:4, CT_Ratio:4, reserved_b2:1, Pulse_Output_Ratio:4, reserved_b3:56, Meter_Time:14, reserved_type:2, reserved_end:4, crc16:2}";


        System.out.println("So let's give it a try...");
        System.out.println("First we open the connection to the meter");
        System.out.println("It will respond with the first 255 bytes of meter data (Request A)");
        send_rec(cmd_request_a);
        JSONObject request_a=parse_response(send_rec(cmd_request_a),tbl_request_a);
        System.out.println(request_a.toString(4) +"\n\n");

        System.out.println("Now we can request other data");
        System.out.println("For example the remaining 255 bytes of meter data (Request B)");
        JSONObject request_b=parse_response(send_rec(cmd_request_b),tbl_request_b);
        System.out.println(request_b.toString(4)+"\n\n");

        System.out.println("When you're finished you will close the connection");
        send_rec(cmd_close);

        // Close connection to serial port
        serialPort.closePort();

    }   catch (SerialPortException ex) {
        System.out.println(ex);
    }
}

public static String byteArrayToHex(String OrgStr) {
   byte[] a = OrgStr.getBytes();
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a){
      sb.append(String.format("%02x", b & 0xff));
      sb.append(" ");
   }
   return sb.toString();
}

}

/*
 *  Sending Meter Request Example (version 1.00)
 *   *  Install json-c if its not installed
 *  https://github.com/json-c/json-c/wiki
 *
 *  Below is command to install json-c on centos
 *  yum install json-c-devel
 *  
 *  Saved this example code to a file named connect.c
 *  - Compile
 *  gcc connect.c -o connect -ljson-c
 *  - Run
 *  ./connect
 */
// Required Includes

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>   /* File Control Definitions */
#include <termios.h> /* POSIX Terminal Control Definitions */
#include <unistd.h>  /* UNIX Standard Definitions */
#include <errno.h>   /* ERROR Number Definitions */
#include <string.h>
#include <json/json.h>

int sp;
// EKM CRC-16 Table
static const unsigned short CRCTABLE[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
};

// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");
unsigned short ekm_calc_crc16(const char *ptr)
{
    unsigned short crc = 0xffff;
    while(*ptr){
        crc = (crc >> 8) ^ CRCTABLE[(crc ^ *ptr) & 0xff];
        ptr++;
    }
    crc = (crc << 8) | (crc >> 8);
    crc &= 0x7f7f;
    return crc;
}

char *substring(char *string, int position, int length)
{
    char *pointer;
    int c;
    pointer = malloc(length+1);
    if (pointer == NULL){
       printf("Unable to allocate memory.\n");
       exit(1);
    }

    for (c = 0 ; c < length ; c++){
       *(pointer+c) = *(string+position);
       string++;
    }
    *(pointer+c) = '\0';
    return pointer;
}

const char* chr(int i)
{
    static char str[1];
    char c = i;
    sprintf(str,"%c", c);
    return str;
}



char* strToHex(const char *ptr){
    char * hex ;
    if((hex = malloc(strlen(ptr)*2+1)) != NULL){
        hex[0] = '\0';
    }

    while(*ptr){
        char tmp[2];
        sprintf(tmp, "%02X ", (unsigned int) *ptr++);
        strcat(hex,tmp);
    }
    return hex;
}

char *concatenate_string(const char *str1,const char *str2)
{
   char *str3;
   int i=0;

   while(str1[i]){
      str3[i]=str1[i];
      i++;
  }
  int k =0;
  while(str2[k]){
      str3[i]=str2[k];
      i++;
      k++;
  }
  str3[i]='\0';
  return str3;
}


// In this code example we make a simple function that will take the command
// you want to send to the meter and make the crc for it before sending.
// After sending we get the response from the meter and check its crc to make sure
// the response is correct
char * send_rec(char send[]){
    char *ss = send;
    // get the the 2nd through the last byte
    // from the send string to create crc
    int crc = ekm_calc_crc16(substring(ss,1, strlen(ss)-1));

    // Convert CRC from int to byte and add to the send request
    ss =concatenate_string(ss,chr((crc >> 8) & 0xFF));
    ss =concatenate_string(ss,chr(crc & 0xFF));

    write(sp,ss,strlen(ss));

    // Get meter response
    char response[255];
    char firstByte[1];
    read(sp, &firstByte[0], sizeof(char));

    if (strcmp(firstByte,"") == 0){
        printf("Meter Instruction: \n");
        printf("%s \n", strToHex(ss));
        printf("Did not trigger a response\n\n");
        return "";
    }

    // The meter returns HEX 06 if ok
    if (strcmp(substring(firstByte,0,1),"\x06") == 0){
         printf("OK.\n\n");
         return "";
    }

    // If the meter returns HEX 02 it's the start of a text read
    if (strcmp(substring(firstByte,0,1),"\x02") == 0){
      response[0] = firstByte[0];
      // Get meter response
      int a =1;
      while(a<255){
          read(sp, &response[a++], sizeof(char));
      }
      // Get check CRC in response (byte 2 through the 3rd from the last)
      // to make sure it's valid
      crc =  ekm_calc_crc16(substring(response,1,strlen(response)-3));
      // Compare our calculated CRC with the resonse CRC
      if ( strcmp(chr((crc >> 8) & 0xFF), substring(response,strlen(response)-2,1) ) ==0 && strcmp(chr(crc & 0xFF), substring(response,strlen(response)-1,1) ) ==0){
          // Return response
          return substring(response,0,strlen(response));
      }
      else{
          return "";
      }
    }

}

// This function takes the response from the meter
// and parses its output into an array using the defined table
json_object * parse_response(char *response,char *table){
    json_object * jsonObject;
    jsonObject = json_tokener_parse(table);

    int pointer=0;

    /*Creating a json object*/
    json_object * response_array = json_object_new_object();

    json_object_object_foreach(jsonObject, key, val) {
        int value = json_object_get_int(val);
        json_object *jstring = json_object_new_string(substring(response,pointer,value));
        json_object_object_add(response_array,key, jstring);
        pointer+=value;
    }    

    return response_array;
}

void main(void){
    // Open connection to serial port
    // Change /dev/ttyUSB0 to match your system
    sp = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY );
    // Error Checking
    if(sp == -1){
       perror("Error! in Opening /dev/ttyUSB0");
       exit(-1);
    }

    // Setting the Attributes of the serial port using termios structure
    struct termios SerialPortSettings;

    //Get the current attributes of the Serial port
    tcgetattr(sp, &SerialPortSettings);

    //Setting the Baud rate
    cfsetispeed(&SerialPortSettings,B9600); /* Set Read  Speed as 9600 */
    cfsetospeed(&SerialPortSettings,B9600); /* Set Write Speed as 9600 */

    SerialPortSettings.c_cflag |=  PARENB;
    SerialPortSettings.c_cflag &= ~CSTOPB;
    SerialPortSettings.c_cflag &= ~CSIZE;
    SerialPortSettings.c_cflag |=  CS7;
    SerialPortSettings.c_cflag &= ~CRTSCTS;
    SerialPortSettings.c_iflag |= IXOFF;
    SerialPortSettings.c_iflag &= ~ICANON;


    // Read buffer size
    SerialPortSettings.c_cc[VMIN] = 0;

    // Set connection timeout
    int timeout = 5;
    SerialPortSettings.c_cc[VTIME] = timeout*10;
    // apply new serial port settings
    tcsetattr(sp,TCSANOW,&SerialPortSettings);
    // discards old data in the buffer
    tcflush(sp, TCIOFLUSH);

    // Meter Number and Password
    char *meter="000300001184";


    // In this example we have assigned
    // some meter request strings to variables
    // Get meter data A request and connection request
    char cmd_request_a[19];
    sprintf(cmd_request_a, "\x2F\x3F%s\x30\x30\x21\x0D\x0A", meter);


    // Get meter data B request
    char cmd_request_b[19];
    sprintf(cmd_request_b, "\x2F\x3F%s\x30\x31\x21\x0D\x0A", meter);

    // Close Connection String
    char *cmd_close="\x01\x42\x30\x03\x75";

    char *tbl_request_a="{ 'reserved_start_byte':1, 'Model':2, 'Firmware':1, 'Meter_Address':12, 'kWh_Tot':8, 'Reactive_Energy_Tot':8, 'Rev_kWh_Tot':8, 'kWh_Ln_1':8, 'kWh_Ln_2':8, 'kWh_Ln_3':8, 'Rev_kWh_Ln_1':8, 'Rev_kWh_Ln_2':8, 'Rev_kWh_Ln_3':8, 'Resettable_kWh_Tot':8, 'Resettable_Rev_kWh_Tot':8, 'RMS_Volts_Ln_1':4, 'RMS_Volts_Ln_2':4, 'RMS_Volts_Ln_3':4, 'Amps_Ln_1':5, 'Amps_Ln_2':5, 'Amps_Ln_3':5, 'RMS_Watts_Ln_1':7, 'RMS_Watts_Ln_2':7, 'RMS_Watts_Ln_3':7, 'RMS_Watts_Tot':7, 'Power_Factor_Ln_1':4, 'Power_Factor_Ln_2':4, 'Power_Factor_Ln_3':4, 'Reactive_Pwr_Ln_1':7, 'Reactive_Pwr_Ln_2':7, 'Reactive_Pwr_Ln_3':7, 'Reactive_Pwr_Tot':7, 'Line_Freq':4, 'Pulse_Cnt_1':8, 'Pulse_Cnt_2':8, 'Pulse_Cnt_3':8, 'State_Inputs':1, 'State_Watts_Dir':1, 'State_Out':1, 'kWh_Scale':1, 'reserved_end_data':2, 'Meter_Time':14, 'reserved_type':2, 'reserved_end':4, 'crc16':2}";
    char *tbl_request_b= "{ 'reserved_start_byte':1, 'Model':2, 'Firmware':1, 'Meter_Address':12, 'kWh_Tariff_1':8, 'kWh_Tariff_2':8, 'kWh_Tariff_3':8, 'kWh_Tariff_4':8, 'Rev_kWh_Tariff_1':8, 'Rev_kWh_Tariff_2':8, 'Rev_kWh_Tariff_3':8, 'Rev_kWh_Tariff_4':8, 'RMS_Volts_Ln_1':4, 'RMS_Volts_Ln_2':4, 'RMS_Volts_Ln_3':4, 'Amps_Ln_1':5, 'Amps_Ln_2':5, 'Amps_Ln_3':5, 'RMS_Watts_Ln_1':7, 'RMS_Watts_Ln_2':7, 'RMS_Watts_Ln_3':7, 'RMS_Watts_Tot':7, 'Power_Factor_Adj_Ln_1':4, 'Power_Factor_Adj_Ln_2':4, 'Power_Factor_Adj_Ln_3':4, 'RMS_Watts_Max_Demand':8, 'Max_Demand_Period':1, 'Pulse_Ratio_1':4, 'Pulse_Ratio_2':4, 'Pulse_Ratio_3':4, 'CT_Ratio':4, 'reserved_b2':1, 'Pulse_Output_Ratio':4, 'reserved_b3':56, 'Meter_Time':14, 'reserved_type':2, 'reserved_end':4, 'crc16':2}";


    printf("So let's give it a try...\n");
    printf("First we open the connection to the meter\n");
    printf("It will respond with the first 255 bytes of meter data (Request A)\n");

    json_object *request_a = parse_response(send_rec(cmd_request_a), tbl_request_a);
    printf("\n%s\n", json_object_to_json_string_ext(request_a,JSON_C_TO_STRING_PRETTY));

    printf("Now we can request other data\n");
    printf("For example the remaining 255 bytes of meter data (Request B)\n");

    json_object *request_b = parse_response(send_rec(cmd_request_b), tbl_request_b);
    printf("\n%s\n", json_object_to_json_string_ext(request_b,JSON_C_TO_STRING_PRETTY));

    printf("When you're finished you will close the connection\n");
    send_rec(cmd_close);
    close(sp);
}

// Parsing Meter Request Example (version 1.00)

// NodeJS requires serialport, async modules to connect with meter
// Install these module from shell prompt
// npm install serialport
// npm install async

// Load modules
var serialport = require("serialport"),
    async = require('async')
;

var SerialPort = serialport.SerialPort;


// EKM CRC-16 Table
var CRCTABLE = [
        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
];


// This function makes use of the CRC table to calulate the CRC
// example: ekm_calc_crc16("PUT_STRING_HERE_YOU_WANT_CRC_OF");

function ekm_calc_crc16(buf){
        var crc = 0xffff;
        for (var i = 0, len = buf.length; i < len; i++) {
          var c = buf[i];
          var index = (crc ^ c.charCodeAt( 0 )) & 0xff;
          var crct = CRCTABLE[index];
          crc=(crc>>8)^crct;
        }
        crc = (crc << 8) | (crc >> 8);
        crc &= 0x7F7F;
        return crc
}


function strToHex(buf) {
    var hex = '';
    for(var i=0;i<buf.length;i++) {
        var pad = ""+buf.charCodeAt(i).toString(16).toUpperCase();
        hex += ' '+ ("00"+pad).slice(-2);
    }
    return hex;
}

// In this code example we make a simple function that will take the command
// you want to send to the meter and make the crc for it before sending.
// After sending we get the response from the meter and check its crc to make sure
// the response is correct
function send_rec(send, callBack){
        // get the the 2nd through the last byte
        // from the send string to create crc
        var crc = ekm_calc_crc16(send.substr(1)) ;
        // Convert CRC from int to byte and add to the send request
        send=send+ String.fromCharCode((crc >> 8) & 0xFF)+String.fromCharCode(crc & 0xFF);

        // Clear response buffer
        response = new Buffer(0);

        // Set Timeout
        var timeout = 5;

        async.series([
         function(callback){
            // Send request
            sp.write(send, function(err, res) {
                if (err) {
                    console.log(err);
                    throw err;
                }
                setTimeout(function() {
                    setImmediate(callback());
                }, timeout*1000);
            });
         },
         function(callback){
            response = response.toString();
            if(response==""){
                console.log("Meter Instruction: \n")
                console.log(strToHex(response));
                console.log("\nDid not trigger a response\n\n")
                setImmediate(callback());
            }else{
                // The meter returns HEX 06 if ok
                if(response.substr(0,1)=="\x06"){
                    console.log("OK.\n\n")
                    setImmediate(callback());
                }
                // If the meter returns HEX 02 it's the start of a text read
                if(response.substr(0,1)=="\x02"){
                    // Get check CRC in response (byte 2 through the 3rd from the last)
                    // to make sure it's valid
                    crc = ekm_calc_crc16(response.substr(1, response.length-3));
                    // Compare our calculated CRC with the resonse CRC
                    if (String.fromCharCode((crc >> 8) & 0xFF)+String.fromCharCode(crc & 0xFF) ==response.substr(-2)){
                        setImmediate(callback());
                    }else{
                        console.log("Meter response CRC failed\n\n");
                        setImmediate(callback());
                    }
                }
            }

          }
         ],function(error){
             if(error){
                 console.log(error);
                 throw error;
             }
             callBack(response);
         });

}

// This function takes the response from the meter
// and parses its output into an array using the defined table

function parse_response(response,table, callback){

    //table_array=JSON.parse(table);
    response_array={};
    pointer=0;
    for (var key in table){
        var val = table[key];
        response_array[key]= response.substr(pointer, val);
        pointer+=val;
    }    

    callback(response_array);
}


// Params for serial port
// Change /dev/ttyUSB0 to match your system
// Open connection to serial port
var sp = new SerialPort("/dev/ttyUSB0", {
        baudRate: 9600,
        parity: 'even',
        dataBits: 7,
        stopBits: 1,
        flowControl: 0,
        bufferSize: 1,
        parser: serialport.parsers.raw
}, false);

// Meter Number and Password
var meter="000300001184";

// In this example we have assigned
// some meter request strings to variables

// Get meter data A request and connection request
var cmd_request_a="\x2F\x3F"+meter+"\x30\x30\x21\x0D\x0A";
// Get meter data B request
var cmd_request_b="\x2F\x3F"+meter+"\x30\x31\x21\x0D\x0A";

// Close Connection String
var cmd_close="\x01\x42\x30\x03\x75";

var tbl_request_a={
"reserved_start_byte":1,
"Model":2,
"Firmware":1,
"Meter_Address":12,
"kWh_Tot":8,
"Reactive_Energy_Tot":8,
"Rev_kWh_Tot":8,
"kWh_Ln_1":8,
"kWh_Ln_2":8,
"kWh_Ln_3":8,
"Rev_kWh_Ln_1":8,
"Rev_kWh_Ln_2":8,
"Rev_kWh_Ln_3":8,
"Resettable_kWh_Tot":8,
"Resettable_Rev_kWh_Tot":8,
"RMS_Volts_Ln_1":4,
"RMS_Volts_Ln_2":4,
"RMS_Volts_Ln_3":4,
"Amps_Ln_1":5,
"Amps_Ln_2":5,
"Amps_Ln_3":5,
"RMS_Watts_Ln_1":7,
"RMS_Watts_Ln_2":7,
"RMS_Watts_Ln_3":7,
"RMS_Watts_Tot":7,
"Power_Factor_Ln_1":4,
"Power_Factor_Ln_2":4,
"Power_Factor_Ln_3":4,
"Reactive_Pwr_Ln_1":7,
"Reactive_Pwr_Ln_2":7,
"Reactive_Pwr_Ln_3":7,
"Reactive_Pwr_Tot":7,
"Line_Freq":4,
"Pulse_Cnt_1":8,
"Pulse_Cnt_2":8,
"Pulse_Cnt_3":8,
"State_Inputs":1,
"State_Watts_Dir":1,
"State_Out":1,
"kWh_Scale":1,
"reserved_end_data":2,
"Meter_Time":14,
"reserved_type":2,
"reserved_end":4,
"crc16":2};

var tbl_request_b={
"reserved_start_byte":1,
"Model":2,
"Firmware":1,
"Meter_Address":12,
"kWh_Tariff_1":8,
"kWh_Tariff_2":8,
"kWh_Tariff_3":8,
"kWh_Tariff_4":8,
"Rev_kWh_Tariff_1":8,
"Rev_kWh_Tariff_2":8,
"Rev_kWh_Tariff_3":8,
"Rev_kWh_Tariff_4":8,
"RMS_Volts_Ln_1":4,
"RMS_Volts_Ln_2":4,
"RMS_Volts_Ln_3":4,
"Amps_Ln_1":5,
"Amps_Ln_2":5,
"Amps_Ln_3":5,
"RMS_Watts_Ln_1":7,
"RMS_Watts_Ln_2":7,
"RMS_Watts_Ln_3":7,
"RMS_Watts_Tot":7,
"Power_Factor_Adj_Ln_1":4,
"Power_Factor_Adj_Ln_2":4,
"Power_Factor_Adj_Ln_3":4,
"RMS_Watts_Max_Demand":8,
"Max_Demand_Period":1,
"Pulse_Ratio_1":4,
"Pulse_Ratio_2":4,
"Pulse_Ratio_3":4,
"CT_Ratio":4,
"reserved_b2":1,
"Pulse_Output_Ratio":4,
"reserved_b3":56,
"Meter_Time":14,
"reserved_type":2,
"reserved_end":4,
"crc16":2};


var response = new Buffer(0);

async.series( [
        function(callback){
             sp.open(function(err) {
                console.log("Connection Opened");
                // Get meter response
                sp.on('data', function(read) {
                    if(response.length<255){
                        response = Buffer.concat([response, read]);
                    }
                });
                setImmediate(callback);
            });
        },
        function(callback){
            console.log("So let's give it a try...\n");
            console.log("First we open the connection to the meter\n");
            console.log("It will respond with the first 255 bytes of meter data (Request A)\n");
            send_rec(cmd_request_a,function(response){
                parse_response(response,tbl_request_a, function(response_a){
                    console.log(JSON.stringify(response_a, null, 4));
                    setImmediate(callback);
                });
            });


        },
        function(callback){
            console.log("Now we can request other data\n");
            console.log("For example the remaining 255 bytes of meter data (Request B)\n");
            send_rec(cmd_request_b,function(response){
                parse_response(response,tbl_request_b, function(response_b){
                    console.log(JSON.stringify(response_b, null, 4));
                    setImmediate(callback);
                });
            });
        },
        function(callback){
            console.log("When you're finished you will close the connection\n");
            send_rec(cmd_close, function(){
                setImmediate(callback);
            });
        }
    ],function (error) {
        if(error){
            console.log(error);
            throw error;
        }
        // Close connection to serial port
        sp.close();
});


The final step. Parsing the meters response

As the final step you can create an array that contains the field names and size of each field and then walk that array to parse the response from the meter to a usable array.

This example, like the other code examples is the simplest way to do this, in order to show you the steps required to get the job done. You can of course build a more robust code base.

In this example we store the table as JSON and use that to parse the response by calling a function named parse_response.

You can find a complete break down of all meter requests and responses in the RS-485 V3 and V4 Requests sections.

RS-485 V3 Only Requests

The requests and responses in this section apply ONLY to v3 Meters

We also have a straight forward C header file that includes definitions for all the responses from the meters and valid values for request parameters. The more advanced programmer may find this useful. The C header can be found here: RS-485 V3 and V4 Requests.

V3 Read Meter Data/Connect

Required v3 request to connect and standard method to read meter

Request Example:

2f 3f (12 byte Meter_Number) 21 0d 0a

Parameter Description
Meter_Number 12 byte Meter_Number. 30 30 30 31 30 30 30 30 31 31 38 84 = 000100001184

Response Table:

Field Name Byte Length Type Scale Type
Reserved_start_byte 1 Byte None
Model 2 Byte None
Firmware 1 Byte None
Meter_Address 12 String None
kWh_Tot 8 Float None
kWh_Tariff_1 8 Float None
kWh_Tariff_2 8 Float None
kWh_Tariff_3 8 Float None
kWh_Tariff_4 8 Float None
Rev_kWh_Tot 8 Float None
Rev_kWh_Tariff_1 8 Float None
Rev_kWh_Tariff_2 8 Float None
Rev_kWh_Tariff_3 8 Float None
Rev_kWh_Tariff_4 8 Float None
RMS_Volts_Ln_1 4 Float Divide By 10
RMS_Volts_Ln_2 4 Float Divide By 10
RMS_Volts_Ln_3 4 Float Divide By 10
Amps_Ln_1 5 Float Divide By 10
Amps_Ln_2 5 Float Divide By 10
Amps_Ln_3 5 Float Divide By 10
RMS_Watts_Ln_1 7 Integer None
RMS_Watts_Ln_2 7 Integer None
RMS_Watts_Ln_3 7 Integer None
RMS_Watts_Tot 7 Integer None
Power_Factor_Adj_Ln_1 4 String None
Power_Factor_Adj_Ln_2 4 String None
Power_Factor_Adj_Ln_3 4 String None
Max_Demand 8 Float None
Max_Demand_Period 1 Integer None
Meter_Time 14 Integer None
CT_Ratio 4 Integer None
Reserved_type 59 Byte None
Reserved_end 4 Byte None
CRC 2 Byte None

RS-485 V4 Only Requests

The requests and responses in this section apply ONLY to v4 Meters

You will notice in a “Response table” for some requests the scale is listed as “kWh_Scale” This means: The scale is based on the kWh_Scale value in the Data A Request. If the kWh_Scale=0 (30 Hex) then no scaling, if it is 1 (31 Hex) then divide by 10, if it is 2 (32 Hex) then divide by 100

We also have a straight forward C header file that includes definitions for all the responses from the meters and valid values for request parameters. The more advanced programmer may find this useful. The C header can be found here: RS-485 V3 and V4 Requests.

V4 Read Meter Data A/Connect

Required v4 request to connect and standard method to read meter date A

Request Example:

2f 3f (12 byte Meter_Number) 30 30 21 0d 0a

Parameter Description
Meter_Number 12 byte Meter_Number. 30 30 30 31 30 30 30 30 31 31 38 84 = 000100001184

Response Table:

Field Name Byte Length Type Scale Type
Reserved_start_byte 1 Byte None
Model 2 Byte None
Firmware 1 Byte None
Meter_Address 12 String None
kWh_Tot 8 Float kWh_Scale
Reactive_Energy_Tot 8 Float kWh_Scale
Rev_kWh_Tot 8 Float kWh_Scale
kWh_Ln_1 8 Float kWh_Scale
kWh_Ln_2 8 Float kWh_Scale
kWh_Ln_3 8 Float kWh_Scale
Rev_kWh_Ln_1 8 Float kWh_Scale
Rev_kWh_Ln_2 8 Float kWh_Scale
Rev_kWh_Ln_3 8 Float kWh_Scale
Resettable_kWh_Tot 8 Float kWh_Scale
Resettable_Rev_kWh_Tot 8 Float kWh_Scale
RMS_Volts_Ln_1 4 Float Divide By 10
RMS_Volts_Ln_2 4 Float Divide By 10
RMS_Volts_Ln_3 4 Float Divide By 10
Amps_Ln_1 5 Float Divide By 10
Amps_Ln_2 5 Float Divide By 10
Amps_Ln_3 5 Float Divide By 10
RMS_Watts_Ln_1 7 Integer None
RMS_Watts_Ln_2 7 Integer None
RMS_Watts_Ln_3 7 Integer None
RMS_Watts_Tot 7 Integer None
Power_Factor_Ln_1 4 String None
Power_Factor_Ln_2 4 String None
Power_Factor_Ln_3 4 String None
Reactive_Pwr_Ln_1 7 Integer None
Reactive_Pwr_Ln_2 7 Integer None
Reactive_Pwr_Ln_3 7 Integer None
Reactive_Pwr_Tot 7 Integer None
Line_Freq 4 Float Divide By 100
Pulse_Cnt_1 8 Integer None
Pulse_Cnt_2 8 Integer None
Pulse_Cnt_3 8 Integer None
State_Inputs 1 Integer None
State_Watts_Dir 1 Integer None
State_Out 1 Integer None
kWh_Scale 1 Integer None
Reserved_end_data 2 Byte None
Meter_Time 14 Integer None
Reserved_type 2 Byte None
Reserved_end 4 Byte None
CRC 2 Byte None

V4 Read Meter Data B

Read meter request for data B

Request Example:

2f 3f (12 byte Meter_Number) 30 31 21 0d 0a

Parameter Description
Meter_Number 12 byte Meter_Number. 30 30 30 31 30 30 30 30 31 31 38 84 = 000100001184

Response Table:

Field Name Byte Length Type Scale Type
Reserved_start_byte 1 Byte None
Model 2 Byte None
Firmware 1 Byte None
Meter_Address 12 String None
kWh_Tariff_1 8 Float kWh_Scale
kWh_Tariff_2 8 Float kWh_Scale
kWh_Tariff_3 8 Float kWh_Scale
kWh_Tariff_4 8 Float kWh_Scale
Rev_kWh_Tariff_1 8 Float kWh_Scale
Rev_kWh_Tariff_2 8 Float kWh_Scale
Rev_kWh_Tariff_3 8 Float kWh_Scale
Rev_kWh_Tariff_4 8 Float kWh_Scale
RMS_Volts_Ln_1 4 Float Divide By 10
RMS_Volts_Ln_2 4 Float Divide By 10
RMS_Volts_Ln_3 4 Float Divide By 10
Amps_Ln_1 5 Float Divide By 10
Amps_Ln_2 5 Float Divide By 10
Amps_Ln_3 5 Float Divide By 10
RMS_Watts_Ln_1 7 Integer None
RMS_Watts_Ln_2 7 Integer None
RMS_Watts_Ln_3 7 Integer None
RMS_Watts_Tot 7 Integer None
Power_Factor_Adj_Ln_1 4 String None
Power_Factor_Adj_Ln_2 4 String None
Power_Factor_Adj_Ln_3 4 String None
RMS_Watts_Max_Demand 8 Float Divide By 10
Max_Demand_Period 1 Integer None
Pulse_Ratio_1 4 Integer None
Pulse_Ratio_2 4 Integer None
Pulse_Ratio_3 4 Integer None
CT_Ratio 4 Integer None
Reserved_b2 1 Byte None
Pulse_Output_Ratio 4 Integer None
Reserved_b3 56 Byte None
Meter_Time 14 Integer None
Reserved_type 2 Byte None
Reserved_end 4 Byte None
CRC 2 Byte None

V4 Set Relay

Set relay state on meter via serial call

Request Example:

01 57 31 02 30 30 38 (1 byte Relay) 28 (1 byte State) (4 byte Seconds) 29 03 (2 byte CRC)

Parameter Description
Relay 11 byte Relay. 31 = Relay 1
1 byte Relay. 32 = Relay 2
State 1 byte State. 30 = Open
1 byte State. 31 = Close
Seconds 4 byte Seconds. 31 32 33 34 = 1234 (0000-9999)
CRC 2 byte CRC

Response: True on completion with 06 return

V4 Set Pulse Input Ratio

Set the pulse input constant on meter over serial line

Request Example:

01 57 31 02 30 30 41 (1 byte Line) 28 (4 byte Ratio) 29 03 (2 byte CRC)

Parameter Description
Line 1 byte Line. 31 = Line 1
1 byte Line. 32 = Line 2
1 byte Line. 33 = Line 3
Ratio 4 byte Ratio. 31 32 33 34 = 1234 (0001-9999)
CRC 2 byte CRC

Response: True on completion with 06 return

V4 Set Pulse Output Ratio

Set pulse output ratio serial call. See current value via read request.

Request Example:

01 57 31 02 30 30 44 34 28 (4 byte Ratio) 29 03 (2 byte CRC)

Parameter Description
Ratio 4 byte Ratio. 30 30 30 31 = 0001
4 byte Ratio. 30 30 30 32 = 0002
4 byte Ratio. 30 30 30 34 = 0004
4 byte Ratio. 30 30 30 35 = 0005
4 byte Ratio. 30 30 30 38 = 0008
4 byte Ratio. 30 30 31 30 = 0010
4 byte Ratio. 30 30 31 36 = 0016
4 byte Ratio. 30 30 32 30 = 0020
4 byte Ratio. 30 30 32 35 = 0025
4 byte Ratio. 30 30 34 30 = 0040
4 byte Ratio. 30 30 35 30 = 0050
4 byte Ratio. 30 30 38 30 = 0080
4 byte Ratio. 30 31 30 30 = 0100
4 byte Ratio. 30 32 30 30 = 0200
4 byte Ratio. 30 34 30 30 = 0400
CRC 2 byte CRC

Response: True on completion with 06 return

V4 Reset Resettable kWh Reverse

Clear the resettable kWhReverse value

Request Example:

01 57 31 02 30 30 44 33 03 (2 byte CRC)

Parameter Description
CRC 2 byte CRC

Response: True on completion with 06 return

V4 Auto Reset Max Demand

Serial call to set the frequency that the v4 meter automatically resets its Max Demand to zero(0).

Request Example:

01 57 31 02 30 30 44 35 28 (1 byte Interval) 29 03 (2 byte CRC)

Parameter Description
Interval 1 byte Interval. 30 = Off
1 byte Interval. 31 = Monthly
1 byte Interval. 32 = Weekly
1 byte Interval. 33 = Daily
1 byte Interval. 34 = Hourly
CRC 2 byte CRC

Response: True on completion with 06 return

V4 Set LCD

Set LCD fields serial call

Request Example:

01 57 31 02 30 30 44 32 28 (40 byte LCD_Setting) 29 03 (2 byte CRC)

You will compose a sting of 80 bytes that contains the LCD items you wish to select (2 bytes per item, 40 items total)

The Hex byte values are listed in the table below for each Item

Item Hex
kWh_Tot 2 byte. 30 31 = 01
Rev_kWh_Tot 2 byte. 30 32 = 02
RMS_Volts_Ln_1 2 byte. 30 33 = 03
RMS_Volts_Ln_2 2 byte. 30 34 = 04
RMS_Volts_Ln_3 2 byte. 30 35 = 05
Amps_Ln_1 2 byte. 30 36 = 06
Amps_Ln_2 2 byte. 30 37 = 07
Amps_Ln_3 2 byte. 30 38 = 08
RMS_Watts_Ln_1 2 byte. 30 39 = 09
RMS_Watts_Ln_2 2 byte. 31 30 = 10
RMS_Watts_Ln_3 2 byte. 31 31 = 11
RMS_Watts_Tot 2 byte. 31 32 = 12
Power_Factor_Ln_1 2 byte. 31 33 = 13
Power_Factor_Ln_2 2 byte. 31 34 = 14
Power_Factor_Ln_3 2 byte. 31 35 = 15
kWh_Tariff_1 2 byte. 31 36 = 16
kWh_Tariff_2 2 byte. 31 37 = 17
kWh_Tariff_3 2 byte. 31 38 = 18
kWh_Tariff_4 2 byte. 31 39 = 19
Rev_kWh_Tariff_1 2 byte. 32 30 = 20
Rev_kWh_Tariff_2 2 byte. 32 31 = 21
Rev_kWh_Tariff_3 2 byte. 32 32 = 22
Rev_kWh_Tariff_4 2 byte. 32 33 = 23
Reactive_Pwr_Ln_1 2 byte. 32 34 = 24
Reactive_Pwr_Ln_2 2 byte. 32 35 = 25
Reactive_Pwr_Ln_3 2 byte. 32 36 = 26
Reactive_Pwr_Tot 2 byte. 32 37 = 27
Line_Freq 2 byte. 32 38 = 28
Pulse_Cnt_1 2 byte. 32 39 = 29
Pulse_Cnt_2 2 byte. 33 30 = 30
Pulse_Cnt_3 2 byte. 33 31 = 31
kWh_Ln_1 2 byte. 33 32 = 32
Rev_kWh_Ln_1 2 byte. 33 33 = 33
kWh_Ln_2 2 byte. 33 34 = 34
Rev_kWh_Ln_2 2 byte. 33 35 = 35
kWh_Ln_3 2 byte. 33 36 = 36
Rev_kWh_Ln_3 2 byte. 33 37 = 37
Reactive_Energy_Tot 2 byte. 33 38 = 38
Max_Demand_Rst 2 byte. 33 39 = 39
Rev_kWh_Rst 2 byte. 34 30 = 40
State_Inputs 2 byte. 34 31 = 41
Max_Demand 2 byte. 34 32 = 42
Parameter Description
LCD_Setting 40 byte LCD_Setting. Use the list above to create 40 byte string
CRC 2 byte CRC

Response: True on completion with 06 return

RS-485 V3 and V4 Requests

#ifndef OMNIMETERS_H
#define OMNIMETERS_H

#define READ_CMD_LEN 6
#define MAX_WRITE_SEASONS 4
#define MAX_HOLIDAYS 20
#define MAX_TARIFFS 4
#define MAX_TARIFF_SCHEDULES 4
#define MAX_MONTHS 6
#define INT_LEN 2

#define READ_PREFIX "2f3f"
#define READ_SUFFIX "3030210d0a"


/* definition of season schedule */
typedef struct tariff_season{
    char start_month[2];
    char start_day[2];
    char schedule[2];
} SEASON_SCHEDULE, *PSEASON_SCHEDULE;

/* comand to set season schedule */
typedef struct write_seasons {
    tariff_season season_schedules[MAX_WRITE_SEASONS];
    char crc[2];
 } WRITE_SEASONS, *PWRITE_SEASONS;

/* definition of holiday weekend schedule set */
typedef struct holiday_weekend{
    char holiday_schedule[2];
    char weekend_schedule[2];
} HOLIDAY_WEEKEND, *PHOLIDAY_WEEKEND;

/* definition of holiday */
typedef struct holiday {
    char holiday_month[2];
    char holiday_day[2];
} HOLIDAY, *PHOLIDAY;

/* command to set holidays */
typedef struct write_holidays {
     char cmd;
     holiday holidays[MAX_HOLIDAYS];
     char crc[2];
} WRITE_HOLIDAYS, *PWRITE_HOLIDAYS;

/* return from to read holidays */
typedef struct read_holidays {
    char cmd[READ_CMD_LEN];
    holiday holidays[MAX_HOLIDAYS];
    holiday_weekend holiday_weekend;
    char reserved[163];
    char crc[2];
} READ_HOLIDAYS, *PREAD_HOLIDAYS;

/* definition of tariff period */
typedef struct tariff{
    char start_hour[2];
    char start_min[2];
    char rate[2];
} TARIFF, *PTARIFF;

/* command to set tariff periods */
typedef struct WRITE_TARIFFS {
    char cmd;
    char schedule[2];
    tariff tariffs[MAX_TARIFFS];
    char crc[2];
} *PWRITE_TARIFFS;

/* the 4 tariffs assigned to a read schedule */
typedef struct schedule_tariffs{
    tariff tariffs[MAX_TARIFFS];
    char reserved[24];
} SCHEDULE_TARIFFS, *PSCHEDULE_TARIFFS;

/* return from read schedules */
typedef struct READ_TARIFFS{
    char cmd[READ_CMD_LEN];
    schedule_tariffs schedule[MAX_TARIFF_SCHEDULES];
    char reserved[44];
    char crc[2];
} *PREAD_TARIFFS;

typedef struct month_tariffs{
    char total[8];
    char tariffs[MAX_TARIFFS][8];
} MONTH_TARIFFS, *PMONTH_TARIFFS;

typedef struct READ_MONTHS {
    char cmd[READ_CMD_LEN];
    month_tariffs months[MAX_MONTHS];
    char crc[2];
} *PREAD_MONTHS;

typedef struct meter_time {
    char year[2];
    char month[2];
    char day[2];
    char weekday[2];
    char hour[2];
    char minute[2];
    char second[2];
} METER_TIME, *PMETER_TIME;

typedef struct read_3a {
    char cmd;
    char Model[2];
    char Firmware[1];
    char Meter_Address[12];
    char kWh_Tot[8];
    char kWh_Tariff_1[8];
    char kWh_Tariff_2[8];
    char kWh_Tariff_3[8];
    char kWh_Tariff_4[8];
    char Rev_kWh_Tot[8];
    char Rev_kWh_Tariff_1[8];
    char Rev_kWh_Tariff_2[8];
    char Rev_kWh_Tariff_3[8];
    char Rev_kWh_Tariff_4[8];
    char RMS_Volts_Ln_1[4];
    char RMS_Volts_Ln_2[4];
    char RMS_Volts_Ln_3[4];
    char Amps_Ln_1[5];
    char Amps_Ln_2[5];
    char Amps_Ln_3[5];
    char RMS_Watts_Ln_1[7];
    char RMS_Watts_Ln_2[7];
    char RMS_Watts_Ln_3[7];
    char RMS_Watts_Tot[7];
    char Cos_Theta_Ln_1[4];
    char Cos_Theta_Ln_2[4];
    char Cos_Theta_Ln_3[4];
    char Max_Demand[8];
    char Max_Demand_Period[1];
    meter_time Meter_Time;
    char CT_Ratio[4];
    char Pulse_Cnt_1[8];
    char Pulse_Cnt_2[8];
    char Pulse_Cnt_3[8];
    char Pulse_Ratio_1[4];
    char Pulse_Ratio_2[4];
    char Pulse_Ratio_3[4];
    char State_Inputs[3];
    char reserved[24];
    char crc[2];

} *PREAD_V3_A;

typedef struct read_4a {
    char cmd;
    char Model[2];
    char Firmware[1];
    char Meter_Address[12];
    char kWh_Tot[8];
    char Reactive_Energy_Tot[8];
    char Rev_kWh_Tot[8];
    char kWh_Ln_1[8];
    char kWh_Ln_2[8];
    char kWh_Ln_3[8];
    char Rev_kWh_Ln_1[8];
    char Rev_kWh_Ln_2[8];
    char Rev_kWh_Ln_3[8];
    char Resettable_kWh_Tot[8];
    char Resettable_Rev_kWh_Tot[8];
    char RMS_Volts_Ln_1[4];
    char RMS_Volts_Ln_2[4];
    char RMS_Volts_Ln_3[4];
    char Amps_Ln_1[5];
    char Amps_Ln_2[5];
    char Amps_Ln_3[5];
    char RMS_Watts_Ln_1[7];
    char RMS_Watts_Ln_2[7];
    char RMS_Watts_Ln_3[7];
    char RMS_Watts_Tot[7];
    char Power_Factor_Ln_1[4];
    char Power_Factor_Ln_2[4];
    char Power_Factor_Ln_3[4];
    char Reactive_Pwr_Ln_1[7];
    char Reactive_Pwr_Ln_2[7];
    char Reactive_Pwr_Ln_3[7];
    char Reactive_Pwr_Tot[7];
    char Line_Freq[4];
    char Pulse_Cnt_1[8];
    char Pulse_Cnt_2[8];
    char Pulse_Cnt_3[8];
    char State_Inputs[1];
    char State_Watts_Dir[1];
    char State_Out[1];
    char kWh_Scale[1];
    char reserved_A[2];
    meter_time Meter_Time;
    char reserved_B[6];
    char crc16[2];
} *PREAD_V4_A;

typedef struct read_4b {
    char cmd;
    char Model[2];
    char Firmware[1];
    char Meter_Address[12];
    char kWh_Tariff_1[8];
    char kWh_Tariff_2[8];
    char kWh_Tariff_3[8];
    char kWh_Tariff_4[8];
    char Rev_kWh_Tariff_1[8];
    char Rev_kWh_Tariff_2[8];
    char Rev_kWh_Tariff_3[8];
    char Rev_kWh_Tariff_4[8];
    char RMS_Volts_Ln_1[4];
    char RMS_Volts_Ln_2[4];
    char RMS_Volts_Ln_3[4];
    char Amps_Ln_1[5];
    char Amps_Ln_2[5];
    char Amps_Ln_3[5];
    char RMS_Watts_Ln_1[7];
    char RMS_Watts_Ln_2[7];
    char RMS_Watts_Ln_3[7];
    char RMS_Watts_Tot[7];
    char Cos_Theta_Ln_1[4];
    char Cos_Theta_Ln_2[4];
    char Cos_Theta_Ln_3[4];
    char RMS_Watts_Max_Demand[8];
    char Max_Demand_Period[1];
    char Pulse_Ratio_1[4];
    char Pulse_Ratio_2[4];
    char Pulse_Ratio_3[4];
    char CT_Ratio[4];
    char reserved_A[1];
    char Pulse_Output_Ratio[4];
    char reserved_B[56];
    meter_time Meter_Time;
    char reserved_C[3];
    char Status_A[1];
    char Status_B[1];
    char Status_C[1];
    char crc16[2];
} *PREAD_V4_B;


typedef enum MaxDemandInterval{
    Off = 0,
    Monthly = 1,
    Weekly = 2,
    Daily = 3,
    Hourly = 4
} MaxDemandIntervalEnum;


typedef enum MaxDemandPeriod {
    At_15_Minutes = 1,
    At_30_Minutes = 2,
    At_60_Minutes = 3
} MaxDemandPeriodEnum;

typedef enum LCDItems {
    kWh_Tot = 1,
    Rev_kWh_Tot = 2,
    RMS_Volts_Ln_1 = 3,
    RMS_Volts_Ln_2 = 4,
    RMS_Volts_Ln_3 = 5,
    Amps_Ln_1 = 6,
    Amps_Ln_2 = 7,
    Amps_Ln_3 = 8,
    RMS_Watts_Ln_1 = 9,
    RMS_Watts_Ln_2 = 10,
    RMS_Watts_Ln_3 = 11,
    RMS_Watts_Tot = 12,
    Power_Factor_Ln_1 = 13,
    Power_Factor_Ln_2 = 14,
    Power_Factor_Ln_3 = 15,
    kWh_Tariff_1 = 16,
    kWh_Tariff_2 = 17,
    kWh_Tariff_3 = 18,
    kWh_Tariff_4 = 19,
    Rev_kWh_Tariff_1 = 20,
    Rev_kWh_Tariff_2 = 21,
    Rev_kWh_Tariff_3 = 22,
    Rev_kWh_Tariff_4 = 23,
    Reactive_Pwr_Ln_1 = 24,
    Reactive_Pwr_Ln_2 = 25,
    Reactive_Pwr_Ln_3 = 26,
    Reactive_Pwr_Tot = 27,
    Line_Freq = 28,
    Pulse_Cnt_1 = 29,
    Pulse_Cnt_2 = 30,
    Pulse_Cnt_3 = 31,
    kWh_Ln_1 = 32,
    Rev_kWh_Ln_1 = 33,
    kWh_Ln_2 = 34,
    Rev_kWh_Ln_2 = 35,
    kWh_Ln_3 = 36,
    Rev_kWh_Ln_3 = 37,
    Reactive_Energy_Tot = 38,
    Max_Demand_Rst = 39,
    Rev_kWh_Rst = 40,
    State_Inputs = 41,
    Max_Demand = 42
} LCDItemsEnum;


typedef enum CTRatio {
    Amps_200 = 200,
    Amps_400 = 400,
    Amps_600 = 600,
    Amps_800 = 800,
    Amps_1000 = 1000,
    Amps_2000 = 2000,
    Amps_3000 = 3000,
    Amps_4000 = 4000,
    Amps_5000 = 5000
} CTRatioEnum;


typedef enum Seasons {
    Season_1 = 0,
    Season_2 = 1,
    Season_3 = 2,
    Season_4 = 3
} SeasonsEnum;


  typedef enum PulseOutput{
    Ratio_1 = 1,
    Ratio_2 = 2,
    Ratio_4 = 4,
    Ratio_5 = 5,
    Ratio_8 = 8,
    Ratio_10 = 10,
    Ratio_16 = 16,
    Ratio_20 = 20,
    Ratio_25 = 25,
    Ratio_40 = 40,
    Ratio_50 = 50,
    Ratio_80 = 80,
    Ratio_100 = 100,
    Ratio_200 = 200,
    Ratio_400 = 400
}PulseOutputEnum;

  typedef enum Pulse{
    Ln1 = 1,
    Ln2 = 2,
    Ln3 = 3
} PulseEnum;

typedef enum Schedules {
    Schedule_1 = 0,
    Schedule_2 = 1,
    Schedule_3 = 2,
    Schedule_4 = 3,
    Schedule_5 = 4,
    Schedule_6 = 5,
    Schedule_7 = 6,
    Schedule_8 = 7
} SchedulesEnum;

typedef enum ReadMonths{
    kWh = 1,
    kWhReverse = 2
} ReadMonthsEnum;


typedef enum DirectionFlag{
    ForwardForwardForward = 1,
    ForwardForwardReverse = 2,
    ForwardReverseForward = 3,
    ReverseForwardForward = 4,
    ForwardReverseReverse = 5,
    ReverseForwardReverse = 6,
    ReverseReverseForward = 7,
    ReverseReverseReverse = 8
} DirectionFlagEnum;

typedef enum ScaleKWH{
    NoScale = 0,
    Scale10 = 1,
    Scale100 = 2
} ScaleKW_Enum;

typedef enum Relay{
    Relay1 = 1,
    Relay2 = 2
} RelayEnum;

typedef enum RelayState {
    RelayOpen = 0,
    RelayClose = 1
} RelayStateEnum;


typedef enum RelayInterval{
    Max = 9999,
    Min = 0,
    Hold = Min
} RelayIntervalEnum;

#endif // OMNIMETERS_H

The requests and responses in this section apply to both v3 and v4 meters. You will notice in a “Response table” for some requests the scale is listed as “kWh_Scale”. This means: V3 Meters have no scaling, V4 Meters will scale based on the kWh_Scale value in the Data A Request. If the kWh_Scale=0 (30 Hex) then no scaling, if it is 1 (31 Hex) then divide by 10, if it is 2 (32 Hex) then divide by 100.

We also have a straight forward C header file that includes definitions for all the responses from the meters and valid values for request parameters. The more advanced programmer may find this useful. The C header can be found here: RS-485 V3 and V4 Requests.

v3/v4 End Communication

Tell the meter your done talking to it.

Request Example:

01 42 30 03 75

There are no parameters or responses for this request.

v3/v4 Send Password

Password authorization call for all settings to meter.

Request Example:

01 50 31 02 28 (8 byte Password) 29 03 (2 byte CRC)

Parameter Description
Password 8 byte Password. Default: 30 30 30 30 30 30 30 30
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Change Password

Reset the meter password. Do not use this function unless the risk of serial line hacking exceeds the risk of pwd loss. There is no recovery backdoor for your meter.

Request Example:

01 57 31 02 30 30 32 30 28 (8 byte New_Password) 29 03 (2 byte CRC)

Parameter Description
New_Password 8 byte New_Password

Response: True on completion with 06 return

v3/v4 Set Max Demand Period

Serial call to set the max demand period for the meter.

Request Example:

01 57 31 02 30 30 35 30 28 (1 byte Period) 29 03 (2 byte CRC)

Parameter Description
Period 1 byte Period. 31 = 15 Minutes
1 byte Period. 32 = 30 Minutes
1 byte Period. 33 = 60 Minutes
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Set Max Demand Reset Value

Serial call to set max demand reset value.

Request Example:

01 57 31 02 30 30 34 30 28 (6 byte Reset_Value) 29 03 (2 byte CRC)

Parameter Description
Reset_Value 6 byte Reset Value. 30 30 30 30 30 30 = Resets to Zero
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Set Time

Set the time on the meter.

Request Example:

01 57 31 02 30 30 36 30 28 (2 byte Year) (2 byte Month) (2 byte Day) (2 byte Day_Of_Week) (2 byte Hours) (2 byte Minutes) (2 bytes Seconds) 29 03 (2 byte CRC)

Parameter Description
Year 2 byte Year. 31 35 = 15 for 2015
Month 2 byte Month. 31 32 = 12 for December
Day 2 byte Day. 32 36 = 26th day of month
Day_Of_Week 2 byte Day_Of_Week. 30 32 = Tuesday (00-06)
Hour 2 byte Hour. 32 32 = 22 (00-23)
Minutes 2 byte Minute. 35 53 = 53 (00-59)
Seconds 2 byte Seconds. 31 30 = 10 (00-59)
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Set CT Ratio

Set the current rating for attached inductive pickup.

Request Example:

01 57 31 02 30 30 44 30 28 (4 byte CT_Ratio) 29 03 (2 byte CRC)

Parameter Description
CT_Ratio 2 byte CT_Ratio. 30 31 30 30 = 100
2 byte CT_Ratio. 30 32 30 30 = 200
2 byte CT_Ratio. 30 34 30 30 = 400
2 byte CT_Ratio. 30 36 30 30 = 600
2 byte CT_Ratio. 30 38 30 30 = 800
2 byte CT_Ratio. 31 30 30 30 = 1000
2 byte CT_Ratio. 31 35 30 30 = 1500
2 byte CT_Ratio. 32 30 30 30 = 2000
2 byte CT_Ratio. 33 30 30 30 = 3000
2 byte CT_Ratio. 34 30 30 30 = 4000
2 byte CT_Ratio. 35 30 30 30 = 5000
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Set Schedule Tariffs

Serial set schedule tariff table.

Request Example:

01 57 31 02 30 30 37 (1 byte Schedule) 28 (2 byte Period_1_Hour) (2 byte Period_1_Minute) (2 byte Period_1_Tariff) (1 byte Schedule) (2 byte Period_2_Hour) (2 byte Period_2_Minute) (2 byte Period_2_Tariff) (1 byte Schedule) (2 byte Period_3_Hour) (2 byte Period_3_Minute) (2 byte Period_3_Tariff) (1 byte Schedule) (2 byte Period_4_Hour) (2 byte Period_4_Minute) (2 byte Period_4_Tariff) 29 03 (2 byte CRC)

Parameter Description
Schedule 1 byte Schedule. 30 = 0 (0-7)
Period_?_Hour 2 byte Hour_(1,2,3,4). 31 37 = 17 (00-23)
Period_?_Minute 2 byte Minute_(1,2,3,4). 34 30 = 40 (00-59)
Period_?_Tariff 2 byte Rate_(1,2,3,4). 30 32 = 02 (00-03)
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Read Schedule Tariffs

Read the schedule tariff and time table (either 1 to 4 or 5 to 8).

Example Request:

01 52 31 02 30 30 37 (1 byte Schedule_Range) 03 (2 byte CRC)

Parameter Description
Schedule_Range 1 byte Schedule_Range. 30 = Periods 1 to 4
1 byte Schedule_Range. 31 = Periods 5 to 6
CRC 2 byte CRC

Response Table:

Field Name Byte Length Type Scale Type
Reserved_echo_cmd 6 Byte None
Schedule_1_Period_1Hour 2 Integer None
Schedule_1_Period_1Min 2 Integer None
Schedule_1_Period_1Tariff 2 Integer None
Schedule_1_Period_2Hour 2 Integer None
Schedule_1_Period_2Min 2 Integer None
Schedule_1_Period_2Tariff 2 Integer None
Schedule_1_Period_3Hour 2 Integer None
Schedule_1_Period_3Min 2 Integer None
Schedule_1_Period_3Tariff 2 Integer None
Schedule_1_Period_4Hour 2 Integer None
Schedule_1_Period_4Min 2 Integer None
Schedule_1_Period_4Tariff 2 Integer None
Reserved_1 24 Byte None
Schedule_2_Period_1Hour 2 Integer None
Schedule_2_Period_1Min 2 Integer None
Schedule_2_Period_1Tariff 2 Integer None
Schedule_2_Period_2Hour 2 Integer None
Schedule_2_Period_2Min 2 Integer None
Schedule_2_Period_2Tariff 2 Integer None
Schedule_2_Period_3Hour 2 Integer None
Schedule_2_Period_3Min 2 Integer None
Schedule_2_Period_3Tariff 2 Integer None
Schedule_2_Period_4Hour 2 Integer None
Schedule_2_Period_4Min 2 Integer None
Schedule_2_Period_4Tariff 2 Integer None
Reserved_2 24 Byte None
Schedule_3_Period_1Hour 2 Integer None
Schedule_3_Period_1Min 2 Integer None
Schedule_3_Period_1Tariff 2 Integer None
Schedule_3_Period_2Hour 2 Integer None
Schedule_3_Period_2Min 2 Integer None
Schedule_3_Period_2Tariff 2 Integer None
Schedule_3_Period_3Hour 2 Integer None
Schedule_3_Period_3Min 2 Integer None
Schedule_3_Period_3Tariff 2 Integer None
Schedule_3_Period_4Hour 2 Integer None
Schedule_3_Period_4Min 2 Integer None
Schedule_3_Period_4Tariff 2 Integer None
Reserved_3 24 Byte None
Schedule_4_Period_1Hour 2 Integer None
Schedule_4_Period_1Min 2 Integer None
Schedule_4_Period_1Tariff 2 Integer None
Schedule_4_Period_2Hour 2 Integer None
Schedule_4_Period_2Min 2 Integer None
Schedule_4_Period_2Tariff 2 Integer None
Schedule_4_Period_3Hour 2 Integer None
Schedule_4_Period_3Min 2 Integer None
Schedule_4_Period_3Tariff 2 Integer None
Schedule_4_Period_4Hour 2 Integer None
Schedule_4_Period_4Min 2 Integer None
Schedule_4_Period_4Tariff 2 Integer None
Reserved_5 79 Byte None
CRC 2 Byte None

v3/v4 Set Seasons

Define the start month and day for each of four seasons and assign a period to each.

Request Example:

01 57 31 02 30 30 38 30 28 (2 byte Season_1_Start_Month) (2 byte Season_1_Start_Day) (2 byte Season_1_Schedule) (2 byte Season_2_Start_Month) (2 byte Season_2_Start_Day) (2 byte Season_2_Schedule) (2 byte Season_3_Start_Month) (2 byte Season_3_Start_Day) (2 byte Season_3_Schedule) (2 byte Season_4_Start_Month) (2 byte Season_5_Start_Day) (2 byte Season_4_Schedule (2 byte CRC)

Parameter Description
Season_?_ Start_Month 2 byte Season_(1,2,3,4)_Start_Month. 31 31 = 11 (01-12)
Season_?_ Start_Day 2 byte Season_(1,2,3,4)_Start_Day. 30 31 = 01 (01-31)
Season_?_Schedule 2 byte Season_(1,2,3,4)_Period. 30 37 = 07 (00-07)
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Set Holidays

Set holiday table on meter. The holiday dates block contains the month and day for up to 20 holiday dates.

Request Example:

01 57 31 02 30 30 42 30 28 (2 byte Holiday_1_Month) (2 byte Holiday_1_Day) (2 byte Holiday_2_Month) (2 byte Holiday_2_Day) (2 byte Holiday_3_Month) (2 byte Holiday_3_Day) (2 byte Holiday_4_Month) (2 byte Holiday_4_Day) (2 byte Holiday_5_Month) (2 byte Holiday_5_Day) (2 byte Holiday_6_Month) (2 byte Holiday_6_Day) (2 byte Holiday_7_Month) (2 byte Holiday_7_Day) (2 byte Holiday_8_Month) (2 byte Holiday_8_Day) (2 byte Holiday_9_Month) (2 byte Holiday_9_Day) (2 byte Holiday_10_Month) (2 byte Holiday_10_Day) (2 byte Holiday_11_Month) (2 byte Holiday_11_Day) (2 byte Holiday_12_Month) (2 byte Holiday_12_Day) (2 byte Holiday_13_Month) (2 byte Holiday_13_Day) (2 byte Holiday_14_Month) (2 byte Holiday_14_Day) (2 byte Holiday_15_Month) (2 byte Holiday_15_Day) (2 byte Holiday_16_Month) (2 byte Holiday_16_Day) (2 byte Holiday_17_Month) (2 byte Holiday_17_Day) (2 byte Holiday_18_Month) (2 byte Holiday_18_Day) (2 byte Holiday_19_Month) (2 byte Holiday_19_Day) (2 byte Holiday_20_Month) (2 byte Holiday_20_Day) 29 03 (2 byte CRC)

Parameter Description
Holiday_?_Month 2 byte Holiday_(1-20)_Month. 31 31 = 11 (01-12)
Holiday_?_Day 2 byte Holiday_(1-20)_Day. 30 31 = 01 (01-31)
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Read Holiday Dates

Serial call to read holidays block from meter.

Request Example:

01 52 31 02 30 30 42 30 03 (2 byte CRC)

Response Table:

Field Name Byte Length Type Scale Type
Reserved_echo_cmd 6 Byte None
Holiday_1_Mon 2 Integer None
Holiday_1_Day 2 Integer None
Holiday_2_Mon 2 Integer None
Holiday_2_Day 2 Integer None
Holiday_3_Mon 2 Integer None
Holiday_3_Day 2 Integer None
Holiday_4_Mon 2 Integer None
Holiday_4_Day 2 Integer None
Holiday_5_Mon 2 Integer None
Holiday_5_Day 2 Integer None
Holiday_6_Mon 2 Integer None
Holiday_6_Day 2 Integer None
Holiday_7_Mon 2 Integer None
Holiday_7_Day 2 Integer None
Holiday_8_Mon 2 Integer None
Holiday_8_Day 2 Integer None
Holiday_9_Mon 2 Integer None
Holiday_9_Day 2 Integer None
Holiday_10_Mon 2 Integer None
Holiday_10_Day 2 Integer None
Holiday_11_Mon 2 Integer None
Holiday_11_Day 2 Integer None
Holiday_12_Mon 2 Integer None
Holiday_12_Day 2 Integer None
Holiday_13_Mon 2 Integer None
Holiday_13_Day 2 Integer None
Holiday_14_Mon 2 Integer None
Holiday_14_Day 2 Integer None
Holiday_15_Mon 2 Integer None
Holiday_15_Day 2 Integer None
Holiday_16_Mon 2 Integer None
Holiday_16_Day 2 Integer None
Holiday_17_Mon 2 Integer None
Holiday_17_Day 2 Integer None
Holiday_18_Mon 2 Integer None
Holiday_18_Day 2 Integer None
Holiday_19_Mon 2 Integer None
Holiday_19_Day 2 Integer None
Holiday_20_Mon 2 Integer None
Holiday_20_Day 2 Integer None
Weekend_Schedule 2 Integer None
Holiday_Schedule 2 Integer None
Reserved_1 163 Byte None
CRC 2 Byte None

v3/v4 Set Schedule for Weekend and Holiday

Set the schedule for weekend and holiday schedules.

Request Example:

01 57 31 02 30 30 43 30 28 (2 byte Weekend_Schedule) (2 byte Holiday_Schedule) 29 03 (2 byte CRC)

Parameter Description
Weekend_Schedule 2 byte Weekend_Schedule. 30 34 = 04 (00-07)
Holiday_Schedule 2 byte Holiday_Schedule. 30 32 = 02 (00-07)
CRC 2 byte CRC

Response: True on completion with 06 return

v3/v4 Read 6 Months

Serial call to read months block from meter.

Request Example:

01 52 31 02 30 30 31 (2 byte kWh_Type) 03 (2 byte CRC)

Parameter Description
kWh_Type 2 byte kWh_Type. 31 = kWh Total
2 byte kWh_Type. 32 = kWh Reverse
CRC 2 byte CRC

Response Table:

Field Name Byte Length Type Scale Type
Reserved_echo_cmd 6 Byte None
Month_1_Ttl 8 Float kWh_Scale
Month_1_Tariff_1 8 Float kWh_Scale
Month_1_Teriff_2 8 Float kWh_Scale
Month_1_Teriff_3 8 Float kWh_Scale
Month_1_Teriff_4 8 Float kWh_Scale
Month_2_Ttl 8 Float kWh_Scale
Month_2_Teriff_1 8 Float kWh_Scale
Month_2_Teriff_2 8 Float kWh_Scale
Month_2_Teriff_3 8 Float kWh_Scale
Month_2_Teriff_4 8 Float kWh_Scale
Month_3_Ttl 8 Float kWh_Scale
Month_3_Teriff_1 8 Float kWh_Scale
Month_3_Teriff_2 8 Float kWh_Scale
Month_3_Teriff_3 8 Float kWh_Scale
Month_3_Teriff_4 8 Float kWh_Scale
Month_4_Ttl 8 Float kWh_Scale
Month_4_Teriff_1 8 Float kWh_Scale
Month_4_Teriff_2 8 Float kWh_Scale
Month_4_Teriff_3 8 Float kWh_Scale
Month_4_Teriff_4 8 Float kWh_Scale
Month_5_Ttl 8 Float kWh_Scale
Month_5_Teriff_1 8 Float kWh_Scale
Month_5_Teriff_2 8 Float kWh_Scale
Month_5_Teriff_3 8 Float kWh_Scale
Month_5_Teriff_4 8 Float kWh_Scale
Month_6_Ttl 8 Float kWh_Scale
Month_6_Teriff_1 8 Float kWh_Scale
Month_6_Teriff_2 8 Float kWh_Scale
Month_6_Teriff_3 8 Float kWh_Scale
Month_6_Teriff_4 8 Float kWh_Scale
Reserved_1 7 Byte None
CRC 2 Byte None

RS-485 ekmmeters.py API

Sending and Parsing Requests using ekmmeters.py

# Sending and Parsing Meter Request Example (version 1.00)

# Requires pyserial module
# pip3 install pyserial

# import ekmmeters module
from ekmmeters import *

# In this code example we make use ekmmeters.py
# with some simple calls to the api you can manage all your meter request

# Open connection to serial port
# Change /dev/ttyUSB0 to match your system
sp = SerialPort("/dev/ttyUSB0", 9600)
if sp.initPort() == False:
    print "Port cannot initialize"
    exit

# Meter Number and Password
# Default password: 00000000
meter="000300001184"
password="00000000"

# In the example we are connecting to a v4 meter
# so we use V4Meter(). If you're connecting to a v3
# meter use V3Meter()
meterV4 = V4Meter(meter)
meterV4.attachPort(sp)

print("So let's give it a try...")
print("First we open the connection to the meter")
print("We use request() it will return a json object")
print("that contains both meter data A and B responses");

if meterV4.request() == True:
    print(meterV4.jsonRender(meterV4.getReadBuffer())+"\n")
else:
    print("Request failed\n")

print("Or request things like the last 6 months of total kwh and reverse kwh")

if meterV4.readMonthTariffs(ReadMonths.kWh) == True:
    mon_buf_kwh = meterV4.getMonthsBuffer(ReadMonths.kWh)
    print(meterV4.jsonRender(mon_buf_kwh))
else:
    print("Request failed\n")

if meterV4.readMonthTariffs(ReadMonths.kWhReverse) == True:
    mon_buf_rev_kwh = meterV4.getMonthsBuffer(ReadMonths.kWhReverse)
    print(meterV4.jsonRender(mon_buf_rev_kwh))
else:
    print("Request failed\n")

print("When requesting information from the meter (reads)")
print("you can send as many requests as you like once you sent")
print("the first connection request (Request A)\n\n")

print("Now let's try sending a setting to the meter")
print("Unlike reading information from the meter")
print("You have to send the connection string and password")
print("The ekmmeters.py API automaticly takes care of this for you");
print("before each setting request\n\n")

print("Now we will loop 3 times opening and closing the relays")

for x in range(1,3):
    print("Open Relay A")
    if meterV4.setRelay(0, Relay.Relay1, RelayState.RelayOpen, password) == True:
        print("OK\n")
    else:
        print("Request failed\n")

    print("Close Relay A")
    if meterV4.setRelay(0, Relay.Relay1, RelayState.RelayClose, password) == True:
        print("OK\n")
    else:
        print("Request failed\n")

    print("Open Relay B")
    if meterV4.setRelay(0, Relay.Relay2, RelayState.RelayOpen, password) == True:
        print("OK\n")
    else:
        print("Request failed\n")

    print("Close Relay B")
    if meterV4.setRelay(0, Relay.Relay2, RelayState.RelayClose, password) == True:
        print("OK\n")
    else:
        print("Request failed\n")

print("When you're finished you will close the connection")
meterV4.serialPostEnd()

# Close connection to serial port
sp.closePort()

If you are coding in Python you can also make use of our ekmmeters.py API

You can find it here https://github.com/ekmmetering/ekmmeters

There are two ways to git it. Using git. Run the following command from your *nix shell:

git clone git://github.com/ekmmetering/ekmmeters

Or using pip by running:

pip install ekmmeters or pip3 install ekmmeters

Another required Python module is pyserial, so make sure to run:

pip install pyserial or pip3 install pyserial

We have created a unittest script named: unittest-ekmmeters.py

To use it, edit the unittest.ini file and update it to match your configuration.

You will also find standard python documentation for the module in two places:

Here: http://ekmmeters.readthedocs.org/en/latest/

Or located in the docs dir.

To build the documentation inside the ekmmeters/docs directory:

cd ekmmeters/docs
pip install sphinx
pip install sphinxcontrib.divparams
pip install bs4
pip install sphinxcontrib-plantuml
pip install sphinxtheme.readability
make html

This will build html docs which will be located in the ekmmeters/docs/_build/html dir.

Just run “make” to see what other document build options are available.

Also check out the Python example using the ekmmeters.py and using Python without using ekmmeters.py

Errors

EKM uses conventional HTTP response codes to indicate the success or failure of an API request. In general, codes in the 2xx range indicate success, codes in the 4xx range indicate an error that failed given the information provided, and codes in the 5xx range indicate an error with EKM’s servers (these are rare). Not all errors map cleanly onto HTTP response codes, however. When a request is valid but does not complete successfully, we return a 402 error code.

The EKM API uses the following error codes:

Error Code Meaning
400 Bad Request – Your request is invalid
401 Unauthorized – Your API key is wrong
402 Request Failed – The parameters were valid but the request failed.
403 Forbidden – The meter requested is hidden for administrators only
404 Not Found – The specified meter could not be found
405 Method Not Allowed – You tried to access a meter with an invalid method
406 Not Acceptable – You requested a format that isn’t html, xml, csv or json
410 Gone – The meter requested has been removed from our servers
418 Unknown
429 Too Many Requests – You’re requesting too many meter readings! Slow down!
500 Internal Server Error – We had a problem with our server. Try again later.
502 Bad Gateway – The proxy server received an invalid response
503 Service Unavailable – We’re temporarily offline for maintenance. Please try again later.