EASTRON SDM120C - RS485 Modbus meter

flane
Posts: 86
Joined: Mon Aug 11, 2014 7:22 am

EASTRON SDM120C - RS485 Modbus meter

Post by flane » Mon Jan 19, 2015 9:01 am

I have made a C program to read the active power via rs485:
(Source: http://kmtronic.com/raspberry-pi-modbus ... ter-2.html )

Code: Select all

include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
 
union
{
char c[4];
float f;
}u;
 
int main(int argc, char *argv[]) {
 
    struct termios serial;
    char inBuff[16];
    char *buffer;
    char choice[5];
    int wcount = 0;
    char cVoltage[] = {0x01,0x04,0x00,0x00,0x00,0x02,0x71,0xCB};
    char cCurrent[] = {0x01,0x04,0x00,0x06,0x00,0x02,0x91,0xCA};
    char cPower[] = {0x01,0x04,0x00,0x0C,0x00,0x02,0xB1,0xC8};
    char cAAPower[] = {0x01,0x04,0x00,0x12,0x00,0x02,0xD1,0xCE};
    char cRAPower[] = {0x01,0x04,0x00,0x18,0x00,0x02,0xF1,0xCC};
    char cPFactor[] = {0x01,0x04,0x00,0x1E,0x00,0x02,0x11,0xCD};
    char cFrequency[] = {0x01,0x04,0x00,0x46,0x00,0x02,0x90,0x1E};
    char cIAEnergy[] = {0x01,0x04,0x00,0x48,0x00,0x02,0xF1,0xDD};
    char cEAEnergy[] = {0x01,0x04,0x00,0x4A,0x00,0x02,0x50,0x1D};
    char cTAEnergy[] = {0x01,0x04,0x01,0x56,0x00,0x02,0x90,0x27};
 
    int fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_NDELAY);
 
    if (fd == -1) {
        perror(argv[2]);
        return -1;
    }
 
    if (tcgetattr(fd, &serial) < 0) {
        perror("Getting configuration");
        return -1;
    }
 
    //////////////////// Set up Serial Configuration ////////////
    serial.c_iflag = 0;
    serial.c_oflag = 0;
    serial.c_lflag = 0;
    serial.c_cflag = 0;
 
    serial.c_cc[VMIN] = 0;
    serial.c_cc[VTIME] = 10;
 
    serial.c_cflag = B2400 | CS8 | PARENB | CREAD & ~PARODD;
    fcntl(fd,F_SETFL,0);
    tcsetattr(fd, TCSANOW, &serial); // Apply configuration
    //////////////////////////////////////////////////////////////
 
    
   int rcount;  
   buffer = &inBuff[0];
   int i = 0;
   int j = 0;
	
///////////////////////////////         Power               ///////////////////////////////
   i = 0; j = 0;
   memset(&inBuff[0],0,sizeof(inBuff));
   write(fd,cPower,sizeof(cPower));
   buffer = &inBuff[0];
   /*
   printf("Sent: ");
   for(j = 0;j < sizeof(cPower);j++)
   {
        printf("%02x ",cPower[j]);
   }
   printf("\r\n");
   printf("Received: " );
   */
        while( (rcount = read(fd,buffer,1)) > 0)
        {
           if (rcount < 0) {
           perror("Read");
           return -1;
        }
        buffer++;
    //    printf("%02x ",inBuff[i]); i++;
  }
    u.c[3] = inBuff[3];
    u.c[2] = inBuff[4];
    u.c[1] = inBuff[5];
    u.c[0] = inBuff[6];

printf("2(%.0f*W)\n",u.f);
///////////////////////////////////////////////////////////////////////////////////////////
close(fd);
}
This is the output with RS485:

Code: Select all

# ./rs485p
2(154*W)
and this is the output of the pulse counter (operating with Metern)

Code: Select all

# java -cp /var/www/metern/comapps/bin/ pv.PoolClient -c live -um W -cn 2
2(158*W)
Metern not accept the ouput from rs485. :?
Can anyone help me? ;)

jeanmarc
Posts: 1600
Joined: Thu Aug 29, 2013 7:16 am

Re: EASTRON SDM120C - RS485 Modbus meter

Post by jeanmarc » Mon Jan 19, 2015 11:15 am

Hi,
The output is correct. What does meterN say in the admin test ?

Pongo
Posts: 30
Joined: Thu Sep 19, 2013 1:18 pm

Re: EASTRON SDM120C - RS485 Modbus meter

Post by Pongo » Tue Jan 20, 2015 3:02 pm

Hello,
I implemented 3 files:
* sdm120c.c: modbus meter reader
* pooler485.php: a modified version of pooler.php
* poolmeters485.sh: wrapper for sdm120.c, called by pooler

I'm testing them, but they almost work :D .

On meterN config_met1.php I have

Code: Select all

$COMMAND1='pooler485 elect 1 1 9600 /dev/ttyUSB0';
for 5' data, and

Code: Select all

$LIVECOMMAND1='more /run/shm/metern1.txt | grep "*W)"';
for live data

You can add other meters:

Code: Select all

$COMMAND2='pooler485 elect 2 2 9600 /dev/ttyUSB0';
$LIVECOMMAND2='more /run/shm/metern2.txt | grep "*W)"';
This is pooler485.php

Code: Select all

#!/usr/bin/php
<?php
if (isset($_SERVER['REMOTE_ADDR'])) {
    die('Direct access not permitted');
}
// This script will output a meterN compatible format for the main command
// You'll need to setup the path to meterN ($pathtomn). Put the meters numbers ($metnum) and the corresponding command ($cmd) :

$pathtomn = '/var/www/metern';
$output = shell_exec('pkill -f poolmeters485.sh > /dev/null 2>&1 &'); // Kill temporary the "live values fetching"

if (isset($argv[1])) {

    $metnum = $argv[2];
    $address = $argv[3];
    $baud = $argv[4];
    $device = $argv[5];

    if ($argv[1] == 'elect') {
        $cmd    = "$pathtomn/comapps/poolmeters485.sh relect $address $baud $device"; // Request counters values during a 5 min period
    } else if ($argv[1] == 'live') {
        $cmd = "$pathtomn/comapps/poolmeters485.sh live $address $baud $device > /dev/null 2>&1 &"; // Restart live fetching at the last counter request
        $output = shell_exec($cmd);
        exit(0);
    } else {
        die('Aborting: no valid argument given\n');
    }
} else {
    die("Usage: pooler485 {elect|live} metnum address baud device\n");
}
// End of setup
define('checkaccess', TRUE);
include("$pathtomn/config/config_main.php");
include("$pathtomn/config/config_met$metnum.php");

// Retrieve previous value in the last daily csv
$dir    = "$pathtomn/data/csv";
$output = glob($dir . '/*.csv');
sort($output);
$csvcnt  = count($output);
$file = file($output[$csvcnt - 1]);
$cnt  = count($file);

if ($cnt==1 && $csvcnt>1) { // Midnight takes yesterday file
$file = file($output[$csvcnt - 2]);
}
$cnt  = count($file);

$i = 0;
while (!isset($prevval)) {
    $i++;
    $array = preg_split('/,/', $file[$cnt - $i]);
    if (!empty($array[$metnum])) {
        $prevval = $array[$metnum];
    }
    if ($i == $cnt) {
        $prevval = 0; // didn't find any prev. value
    }
}
sleep(1); // oh why ?
// Now retrieve the current value
$datareturn = shell_exec($cmd);
$datareturn = trim($datareturn);
$datareturn = preg_replace("/^${'ID'.$metnum}\(/i", '', $datareturn); // VALUE*UNIT)
$lastval    = preg_replace("/\*[a-z0-9]+\)$/i", '', $datareturn); // VALUE

settype($lastval, 'float');
settype($prevval, 'float');
if ($lastval == 0)
    $lastval = $prevval;

if (${'PASSO' . $metnum} > 0 && $lastval > ${'PASSO' . $metnum}) { // counter pass over
    $lastval -= ${'PASSO' . $metnum};
}
$lastval = round($lastval, ${'PRECI' . $metnum});
$str     = utf8_decode("${'ID'.$metnum}($lastval*${'UNIT'.$metnum})\n");
echo "$str";

?>
This is poolmeters485.sh

Code: Select all

#!/bin/bash

ADDRESS="$2"
BAUD_RATE="$3"
DEVICE="$4"

TYPE="$1"

if [ "$TYPE" == 'relect' ]; then

    VALUE_ENERGY=`sdm120c -a ${ADDRESS} -b ${BAUD_RATE} -i -m ${DEVICE}`
    echo ${VALUE_ENERGY}

elif [ "$TYPE" == 'live' ]; then

    while [ true ]; do
        VALUE_LIVE=`sdm120c -a ${ADDRESS} -b ${BAUD_RATE} -p -m ${DEVICE}`
        #echo ${VALUE_LIVE}
        echo ${VALUE_LIVE} > /run/shm/metern${ADDRESS}.txt
        sleep 2s
    done

fi
And this is sdm120c.c

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif

/*
 * sdm120c: ModBus RTU client to read EASTRON SDM120C smart mini power meter registers
 *
 * Copyright (C) 2015 Gianfranco Di Prinzio < gianfrdp [@] inwind [.] it >
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <getopt.h>

#include <modbus.h>

#define DEFAULT_RATE 2400

// Read
#define VOLTAGE   0x0000
#define CURRENT   0x0006
#define POWER     0x000C
#define APOWER    0x0012
#define RAPOWER   0x0018
#define PFACTOR   0x001E
#define FREQUENCY 0x0046
#define IAENERGY  0x0048
#define EAENERGY  0x004A
#define TAENERGY  0x0156

// Write
#define DEVICE_ID 0x0014
#define BAUD_RATE 0x001C

#define BR1200 5
#define BR2400 0
#define BR4800 1
#define BR9600 2

const char *version = "1.0.1";

void usage(char* program) {
    printf("sdm120c %s: ModBus RTU client to read EASTRON SDM120C smart mini power meter registers\n",version);
    printf("Copyright (C) 2015 Gianfranco Di Prinzio < gianfrdp [@] inwind [.] it>\n\n");
    printf("Usage: %s [-a address] [-p] [-v] [-c] [-e] [-i] [-t] [-f] [-g] [-m] [-b baud_rate] device\n", program);
    printf("       %s [-a address] -s new_address device\n", program);
    printf("       %s [-a address] -r baud_rate device\n\n", program);
    printf("where\n");
    printf("\t-a address \tMeter number (between 1 and 247). Default: 1\n");
    printf("\t-s new_address \tSet new meter number (between 1 and 247)\n");
    printf("\t-p \t\tGet power (W)\n");
    printf("\t-v \t\tGet voltage (V)\n");
    printf("\t-c \t\tGet current (A)\n");
    printf("\t-f \t\tGet frequency (Hz)\n");
    printf("\t-g \t\tGet power factor\n");
    printf("\t-e \t\tGet exported energy (kWh)\n");
    printf("\t-i \t\tGet imported energy (kWh)\n");
    printf("\t-t \t\tGet total energy (kWh)\n");
    printf("\t-b baud_rate \tUse baud_rate serial port speed (1200, 2400, 4800, 9600)\n");
    printf("\t\t\tDefault: 2400\n");
    printf("\t-r baud_rate \tSet baud_rate meter speed (1200, 2400, 4800, 9600)\n");
    printf("\t-m \t\tOutput values in IEC 62056 format ID(VALUE*UNIT)\n");
    printf("\tdevice\t\tSerial device, i.e. /dev/ttyUSB0\n\n");
    printf("Serial device is required. When no parameter is passed, retrives all values\n");
}

float getMeasure(modbus_t *ctx, int address) {

    int nb = 2;
    uint16_t tab_reg[nb * sizeof(uint16_t)];
    int rc;
    int i;
    
    //printf("Register Address %d [%04X]\n", 30000+address+1, address); 
    rc = modbus_read_input_registers(ctx, address, nb, tab_reg);

    if (rc == -1) {
      fprintf(stderr, "ERROR %s\n", modbus_strerror(errno));
      modbus_close(ctx);
      modbus_free(ctx);
      exit(EXIT_FAILURE);
    }
    
    /*
    for (i=0; i < rc; i++) {
      printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
    }
    */

    // BUG in libmodbus: swap LSB and MSB
    uint16_t tmp1 = tab_reg[0];
    uint16_t tmp2 = tab_reg[1];
    tab_reg[0] = tmp2;
    tab_reg[1] = tmp1;

    float value = modbus_get_float(&tab_reg[0]);

    return value;

}

void changeAddress(modbus_t *ctx, int new_addr)
{
    int nb = 2;
    uint16_t tab_reg[nb * sizeof(uint16_t)];

    modbus_set_float((float) new_addr, &tab_reg[0]);
    // BUG in libmodbus: swap LSB and MSB
    uint16_t tmp1 = tab_reg[0];
    uint16_t tmp2 = tab_reg[1];
    tab_reg[0] = tmp2;
    tab_reg[1] = tmp1;
    
    int n = modbus_write_registers(ctx, DEVICE_ID, nb, tab_reg);
    if (n != -1) {
        printf("New address %d\n", new_addr);
    } else {
        printf("errno: %s, %d, %d\n", modbus_strerror(errno), errno, n);
        modbus_close(ctx);
        modbus_free(ctx);
        exit(EXIT_FAILURE);
    }
}

void changeBaudRate(modbus_t *ctx, int new_rate)
{
    int nb = 2;
    uint16_t tab_reg[nb * sizeof(uint16_t)];

    modbus_set_float((float) new_rate, &tab_reg[0]);
    // BUG in libmodbus: swap LSB and MSB
    uint16_t tmp1 = tab_reg[0];
    uint16_t tmp2 = tab_reg[1];
    tab_reg[0] = tmp2;
    tab_reg[1] = tmp1;
    
    int n = modbus_write_registers(ctx, BAUD_RATE, nb, tab_reg);
    if (n != -1) {
        printf("New baud_rate %d\n", new_rate);
    } else {
        printf("errno: %s, %d, %d\n", modbus_strerror(errno), errno, n);
        modbus_close(ctx);
        modbus_free(ctx);
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char* argv[])
{

    int device_address = 1;
    int new_address    = 0;
    int power_flag     = 0;
    int volt_flag      = 0;
    int current_flag   = 0;
    int freq_flag      = 0;
    int pf_flag        = 0;
    int apower_flag    = 0;
    int rapower_flag   = 0;
    int export_flag    = 0;
    int import_flag    = 0;
    int total_flag     = 0;
    int baud_rate      = 0;
    int new_baud_rate  = 0;
    int metern_flag    = 0;
    int count_param    = 0;
    int index;
    int c;
    char *device = NULL;
    int speed = 0;

    if (argc == 1) {
        usage(argv[0]);
        exit(EXIT_FAILURE);
    }

    opterr = 0;

    while ((c = getopt (argc, argv, "a:b:cefgilmnpr:s:tv")) != -1) {
        switch (c)
        {
            case 'a':
                device_address = atoi(optarg);
                if (!(0 < device_address && device_address <= 247)) {
                    fprintf (stderr, "Address must be between 1 and 247.\n");
                    exit(EXIT_FAILURE);
                }
                break;
            case 'v':
                volt_flag = 1;
                count_param++;
                break;
            case 'p':
                power_flag = 1;
                count_param++;
                break;
            case 'c':
                current_flag = 1;
                count_param++;
                break;
            case 'e':
                export_flag = 1;
                count_param++;
                break;
            case 'i':
                import_flag = 1;
                count_param++;
                break;
            case 't':
                total_flag = 1;
                count_param++;
                break;
            case 'f':
                freq_flag = 1;
                count_param++;
                break;
            case 'g':
                pf_flag = 1;
                count_param++;
                break;
            case 'l':
                apower_flag = 1;
                count_param++;
                break;
            case 'n':
                rapower_flag = 1;
                count_param++;
                break;
            case 'b':
                speed = atoi(optarg);
                if (speed == 1200 || speed == 2400 || speed == 4800 || speed == 9600) {
                    baud_rate = speed;
                } else {
                    fprintf (stderr, "Baud Rate must be one of 1200, 2400, 4800, 9600\n");
                    exit(EXIT_FAILURE);
                }
                break;
            case 'r':
                speed = atoi(optarg);
                switch (speed) {
                    case 1200:
                        new_baud_rate = BR1200;
                        break;
                    case 2400:
                        new_baud_rate = BR2400;
                        break;
                    case 4800:
                        new_baud_rate = BR4800;
                        break;
                    case 9600:
                        new_baud_rate = BR9600;
                        break;
                    default:
                        fprintf (stderr, "Baud Rate must be one of 1200, 2400, 4800, 9600\n");
                        exit(EXIT_FAILURE);
                }
                break;
            case 's':
                new_address = atoi(optarg);
                if (!(0 < new_address && new_address <= 247)) {
                    fprintf (stderr, "New address must be between 1 and 247.\n");
                    exit(EXIT_FAILURE);
                }
                break;
            case 'm':
                metern_flag = 1;
                break;
            case '?':
                if (optopt == 'a' || optopt == 'b' || optopt == 's') {
                    fprintf (stderr, "Option -%c requires an argument.\n", optopt);
                    usage(argv[0]);
                    exit(EXIT_FAILURE);
                }
                else if (isprint (optopt)) {
                    fprintf (stderr, "Unknown option `-%c'.\n", optopt);
                    usage(argv[0]);
                    exit(EXIT_FAILURE);
                }
                else {
                    fprintf (stderr,"Unknown option character `\\x%x'.\n",optopt);
                    usage(argv[0]);
                    exit(EXIT_FAILURE);
                }
            default:
                fprintf (stderr, "Unknown option `-%c'.\n", optopt);
                usage(argv[0]);
                exit(EXIT_FAILURE);
        }
    }

    if ((optind+1) > argc) {
        /* need at least one argument (change +1 to +2 for two, etc. as needeed) */
        //printf("optind = %d, argc = %d\n", optind, argc);
        usage(argv[0]);
        exit(EXIT_FAILURE);
    } else {
        device = argv[optind];
    }

    if (count_param > 1 && metern_flag == 1) {
        fprintf(stderr, "Only one of the parameters between -p, -v, -c, -f, -g, -l, -n, -e, -i or -t is allowed with -m\n\n");
        usage(argv[0]);
        exit(EXIT_FAILURE);
    }
    
    modbus_t *ctx;
    if (baud_rate == 0) baud_rate = DEFAULT_RATE;

    ctx = modbus_new_rtu(device, baud_rate, 'E', 8, 1);

    if (ctx == NULL) {
        fprintf(stderr, "Unable to create the libmodbus context\n");
        exit(EXIT_FAILURE);
    }

    //modbus_set_debug(ctx, 1);
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec =  10000;
    modbus_set_byte_timeout(ctx, &timeout);

    if (modbus_connect(ctx) == -1) {
        fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        exit(EXIT_FAILURE);
    }

    modbus_set_slave(ctx, device_address);

    float voltage    = 0;
    float current    = 0;
    float power      = 0;
    float apower     = 0;
    float rapower    = 0;
    float pf         = 0;
    float freq       = 0;
    float imp_energy = 0;
    float exp_energy = 0;
    float tot_energy = 0;

    if (new_address > 0 && new_baud_rate > 0) {
        
        fprintf(stderr, "Parameter -s and -r are mutually exclusive\n\n");
        usage(argv[0]);
        modbus_close(ctx);
        modbus_free(ctx);
        exit(EXIT_FAILURE);
        
    } else if (new_address > 0) {
    
        if (count_param > 0) {
            usage(argv[0]);
            modbus_close(ctx);
            modbus_free(ctx);
            exit(EXIT_FAILURE);
        } else {
            changeAddress(ctx, new_address);
            modbus_close(ctx);
            modbus_free(ctx);
            return 0;
        }
        
    } else if (new_baud_rate > 0) {
    
        if (count_param > 0) {
            usage(argv[0]);
            modbus_close(ctx);
            modbus_free(ctx);
            exit(EXIT_FAILURE);
        } else {
            changeBaudRate(ctx, new_baud_rate);
            modbus_close(ctx);
            modbus_free(ctx);
            return 0;
        }
        
    } else if (power_flag   == 0 &&
               volt_flag    == 0 &&
               current_flag == 0 &&
               freq_flag    == 0 &&
               pf_flag      == 0 &&
               export_flag  == 0 &&
               import_flag  == 0 &&
               total_flag   == 0
       ) {
       // if no parameter, retrieve all values
        power_flag   = 1;
        volt_flag    = 1;
        current_flag = 1;
        freq_flag    = 1;
        pf_flag      = 1;
        export_flag  = 1;
        import_flag  = 1;
        total_flag   = 1;
    }

    if (volt_flag == 1) {
        voltage = getMeasure(ctx, VOLTAGE);
        if (metern_flag == 1) {
            printf("%d(%4.2f*V)\n", device_address, voltage);
        } else {
            printf("Voltage: %4.2f V \n\n",voltage);
        }
    }

    if (current_flag == 1) {
        current  = getMeasure(ctx, CURRENT);
        if (metern_flag == 1) {
            printf("%d(%4.2f*A)\n", device_address, current);
        } else {
            printf("Current: %4.2f A \n\n",current);
        }
    }

     if (power_flag == 1) {
        power = getMeasure(ctx, POWER);
        if (metern_flag == 1) {
            printf("%d(%4.2f*W)\n", device_address, power);
        } else {
            printf("Power: %4.2f W \n\n", power);
        }
    }

    if (apower_flag == 1) {
        apower = getMeasure(ctx, APOWER);
        if (metern_flag == 1) {
            printf("%d(%4.2f*VA)\n", device_address, apower);
        } else {
            printf("Active Apparent Power: %4.2f VA \n\n", apower);
        }
    }

    if (rapower_flag == 1) {
        rapower = getMeasure(ctx, RAPOWER);
        if (metern_flag == 1) {
            printf("%d(%4.2f*VAr)\n", device_address, rapower);
        } else {
            printf("Reactive Apparent Power: %4.2f VAr \n\n", rapower);
        }
    }

    if (pf_flag == 1) {
        pf = getMeasure(ctx, PFACTOR);
        if (metern_flag == 1) {
            printf("%d(%4.2f*-)\n", device_address, pf);
        } else {
            printf("Power Factor: %4.2f \n\n", pf);
        }
    }

    if (freq_flag == 1) {
        freq = getMeasure(ctx, FREQUENCY);
        if (metern_flag == 1) {
            printf("%d(%4.2f*Hz)\n", device_address, freq);
        } else {
            printf("Frequency: %4.2f Hz \n\n", freq);
        }
    }

    if (import_flag == 1) {
        imp_energy = getMeasure(ctx, IAENERGY);
        if (metern_flag == 1) {
            printf("%d(%d*Wh)\n", device_address, (int)(imp_energy*1000));
        } else {
            printf("Import Active Energy: %4.2f kWh \n\n", imp_energy);
        }
    }

    if (export_flag == 1) {
        exp_energy = getMeasure(ctx, EAENERGY);
        if (metern_flag == 1) {
            printf("%d(%d*Wh)\n", device_address, (int)(exp_energy*1000));
        } else {
            printf("Export Active Energy: %4.2f kWh \n\n", exp_energy);
        }
    }

    if (total_flag == 1) {
        tot_energy = getMeasure(ctx, TAENERGY);
        if (metern_flag == 1) {
            printf("%d(%d*Wh)\n", device_address, (int)(tot_energy*1000));
        } else {
            printf("Total Active Energy: %4.2f kWh \n\n", tot_energy);
        }
    }


    modbus_close(ctx);
    modbus_free(ctx);

    return 0;
}

#ifdef __cplusplus
}
#endif

To compile on Raspbian

Code: Select all

sudo apt-get install libmodbus-dev
gcc `pkg-config --cflags libmodbus` -c -o sdm120c.o sdm120c.c
gcc  -o sdm120c sdm120c.o `pkg-config --libs libmodbus`
Runing

Code: Select all

pi@raspberrypi ~/modbus/sdm120c-1.0.1 $ sdm120c -a 2 -b 9600 -p -m /dev/ttyUSB0
2(40.20*W)
pi@raspberrypi ~/modbus/sdm120c-1.0.1 $ sdm120c -a 2 -b 9600 -i -m /dev/ttyUSB0
2(1019*Wh)

jeanmarc
Posts: 1600
Joined: Thu Aug 29, 2013 7:16 am

Re: EASTRON SDM120C - RS485 Modbus meter

Post by jeanmarc » Tue Jan 20, 2015 6:30 pm

I don't think you need a 'pooler485.php'. If your meter can return the total quantities counters, it is not needed.
The pooler.php is a trick because the arduino variables counting is limited and mostly because they reset to zero after a surge.
The trick is to read a virtual counter and increment with the counting during a 5min lapse.

But you indeed need to stop the infinite live filling when requesting the 'Main command' (Since you can only make one query at a time on a RS485 bus)
:idea: I don't know what return your meter exactly but another idea would be to request all values (Live + Main) all the time, fill a tmps memory and simply ask meterN to look from there.

ps: I can host your com app files if you wish.

Pongo
Posts: 30
Joined: Thu Sep 19, 2013 1:18 pm

Re: EASTRON SDM120C - RS485 Modbus meter

Post by Pongo » Wed Jan 21, 2015 2:39 am

Good idea.
I implemented a new pooler that saves power and energy on a temp file: pooler485.sh
The program can retrieve one or more value at the same time (power and energy).

Code: Select all

$ sdm120c -a 2 -b 9600 -m /dev/ttyUSB0
2(229.60*V)
2(0.15*A)
2(37.50*W)
2(0.86*-)
2(50.00*Hz)
2(1193*Wh)
2(0*Wh)
2(1193*Wh)

Code: Select all

$ sdm120c -a 2 -b 9600 /dev/ttyUSB0
Voltage: 229.80 V
Current: 0.13 A
Power: 35.00 W
Power Factor: 0.80
Frequency: 50.00 Hz
Import Active Energy: 1195 Wh
Export Active Energy: 0 Wh
Total Active Energy: 1195 Wh
The pooler can be used also to read more meters on the same RS485 bus, for example

Code: Select all

pooler485 1,2,3 9600 /dev/ttyUSB0
pooler485.sh

Code: Select all

#!/bin/bash
ADDRESSES="$1"
BAUD_RATE="$2"
DEVICE="$3"
ADDR_ARR=$(echo $ADDRESSES | tr "," "\n")
while [ true ]; do
  ID=0
  for ADDRESS in $ADDR_ARR
  do
     #ID=$ADDRESS
     CMD="sdm120c -a ${ADDRESS} -b ${BAUD_RATE} -i -p -m ${DEVICE}"
     VALUE=`$CMD`
     echo -e "$VALUE" > /run/shm/metern${ADDRESS}.txt
     sleep 2s
  done
done

Code: Select all

$COMMAND1='more /run/shm/metern1.txt | egrep "^1\(" | grep "*Wh)"';
$LIVECOMMAND1='more /run/shm/metern1.txt  | egrep "^1\(" | grep "*W)"';
I'm testing it.

I created a repository here http://github.com/gianfrdp/SDM120C

You can add my files on meterN

I'm going to implement also a protocol for 123solar to, so it could be used for every not supported inverter, at cost of about $25 :D

Thank you

jeanmarc
Posts: 1600
Joined: Thu Aug 29, 2013 7:16 am

Re: EASTRON SDM120C - RS485 Modbus meter

Post by jeanmarc » Wed Jan 21, 2015 7:49 am

Hi,
I don't put any com apps in meterN (except the example). It is nice you hosts your files on github.

What worry me with this really cheap Eastron meter is the lack of certification (Chinese). If your house burn from an electrical problem, i am unsure the assurance will cover the havoc :?
I am "lurking" to change my pulse meter with a ABB B21-112 but it cost around 130€ :|
I am still searching a brand-named cheaper meter with RS485 output.

Thanks for your work

flane
Posts: 86
Joined: Mon Aug 11, 2014 7:22 am

Re: EASTRON SDM120C - RS485 Modbus meter

Post by flane » Wed Jan 21, 2015 6:08 pm

Hi Jeanmarc,
This is the CE declaration for EASTRON SDM120*
https://www.gov.uk/government/uploads/s ... ssue_1.pdf
under European Directive 2004/22/CE

The Model SDM120C is not specified, but there is talk of the family sdm120 *

jeanmarc
Posts: 1600
Joined: Thu Aug 29, 2013 7:16 am

Re: EASTRON SDM120C - RS485 Modbus meter

Post by jeanmarc » Wed Jan 21, 2015 8:03 pm

Well CE certification mean nothing. For example, you can certify a smoke detector for the vibrations or EMC and have a CE label, it don't mean it is able to detect smoke :x
CE is often renamed as Chinese Export (no offense).. I'll look further if Eastron products are "safe".. i'am intrested in the SDM220 ;)

jeanmarc
Posts: 1600
Joined: Thu Aug 29, 2013 7:16 am

Re: EASTRON SDM120C - RS485 Modbus meter

Post by jeanmarc » Wed Jan 21, 2015 8:51 pm

'just purchased a SDM220 from here, i'am interested to get precise bi-directionnal measurement (and get ride of eflow)
ps: I hope my house won't brun :D

flane
Posts: 86
Joined: Mon Aug 11, 2014 7:22 am

Re: EASTRON SDM120C - RS485 Modbus meter

Post by flane » Thu Jan 22, 2015 5:54 pm

Sorry, the previous was the MID certification and not CE.

This is the CE certification for SDM120 and also for SDM220
https://dl.dropboxusercontent.com/u/553 ... ficate.pdf
under Low Voltage Directive 2006/95/CE and EMC Directive 2004/108/EC Annex II

Also SDM120C is a bi-directional measurement.

For me, EASTRON is serious ....
Normally you don't find any documentation for these Chinese devices ....

PS: I hope so too .... :D

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest