vitt76

 
<<< Back

Снятие показаний со счетчика Меркурий 206

Решение для majordomo

На авторство не претендую, всего лишь собрал разрозненные данные по сети и отмел откровенно неработающие.
Сразу скажу, что ни один готовый модуль счетчиков из маркета с моим 206 нормально не заработал, равно не заработали и многочисленные python и php-скрипты, которые я находил на форуме и в интернете.
Для начала, я уверен, что вы уже ознакомились с базовой статьей уважаемого directman66
https://connect.smartliving.ru/profile/1502/blog/k...
и уже имеете конвертер rs-485 на USB и установили правильные драйвера и ser2net на ваше устройство, об этом всем можно прочитать в статье выше.
В качестве устройства можно использовать роутер, малину/апельсину с установленным majordomo или без, я использую orange pi zero, на следующем этапе хочу заменить ее на ESP или Wemos.
Также, понадобится серийный номер счетчика, он написан на его морде, а программно получить у вас его не удастся, даже через стандартное приложение под винду.
Следующим шагом нам понадобится правильная строка инициализации порта для ser2net, на стандартных - не взлетит.
Вот она
3020:raw:0:/dev/ttyUSB0:9600 NONE 1STOPBIT 8DATABITS -XONXOFF -LOCAL -RTSCTS
Мне подсказал ее не менее уважаемый stellhawk с форума mjdm.
Далее, адаптируем php-скрипт отсюда, это единственное, что у меня сработало с 206
https://github.com/stell-hawk/mercury206/blob/mast...

DEFINE('MDEBUG',FALSE);
$error = "";
$error1 = "";
class mercury206_lib
{
    public $socket;
    public $host;
    public $port;
    public $addr;
    public $connected=false;
    public $timeout=1;

function __construct($host,$port,$addr)
{
$this->host=$host;
$this->port=$port;
$this->addr=str_pad(dechex($addr),8,"0",STR_PAD_LEFT);
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$this->socket) $error='Unable to create socket';
socket_set_option($this->socket,SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$this->timeout, "usec"=>0));
//echo 'created socket'.$this->socket.'\n';
$this->connected=@socket_connect($this->socket,$this->host,$this->port);
if (!$this->connected) $error='Unable to connect to '.$this->host.":".$this->port;

}
function send($in)
{
        $in=hex2bin($in);
    socket_send($this->socket,$in,strlen($in),MSG_EOF);
    $ret=socket_read($this->socket,1024);
    //шлем дополнительную команду чтобы счетчик не разорвал соединение (актуально только для чтения кВт*ч - там слишком большой ответ) в остальных случаях можно и не слать
    socket_send($this->socket,hex2bin("f2"),strlen($in),MSG_EOF);
    $ret.=socket_read($this->socket,1024);
    return bin2hex($ret);
}

function send_ret($code)
{
    $msg=$this->addr.$code;
    $msg=$msg.$this->crc16_modbus($msg);
    if(MDEBUG)echo "\nsend: ".$this->nice_hex($msg)."\n";
    $ret=$this->send($msg);
    if(MDEBUG)echo "\nret(".strlen($ret).") :".$this->nice_hex($ret)."\n";
    return $ret=$this->decoderet($ret);
}

//Получить серийный номер
function get_serial()
{
    $ret=$this->send_ret("2F");
    if(!$ret)return false;
    return hexdec(implode("",$ret));
}

//Заряд батареи
function get_battery()
{
    $ret=$this->send_ret("29");
    if(!$ret)return false;
    return implode(".",$ret);
}

//мгновенные значения (U,I,P)
function get_uip()
{
    $ret=$this->send_ret("63");
    if(!$ret)return false;
    $U=$ret[5].$ret[6];
    $I=$ret[7].$ret[8];
    $P=$ret[9].$ret[10].$ret[11];
    //$out['U']=$U[0].$U[1].$U[2].".".$U[3];
    $out['U']=(($U[0]*1000.0) + ($U[1]*100.0) + ($U[2]*10.0) + ($U[3]*1.0)) / 10.0;
    //$out['mI']=$I[0].$I[1].".".$I[2].$I[3];
    $out['mA']=(($I[0]*1000.0) + ($I[1]*100.0) + ($I[2]*10.0) + ($I[3]*1.0)) * 10.0;    //вывод значения тока в mA/ч
    $out['I']=( (($I[0]*1000.0) + ($I[1]*100.0) + ($I[2]*10.0) + ($I[3]*1.0)) * 10.0 ) / 1000.0;    //вывод значения тока в A/ч
    //$out['P']=$P[0].$P[1].$P[2].".".$P[3].$P[4].$P[5];
    $out['W']=( ($P[0]*100000.0) + ($P[1]*10000.0) + ($P[2]*1000.0) + ($P[3]*100.0) + ($P[4]*10.0) + ($P[5]*1.0) ); //вывод значения мощности в Вт/ч
    $out['P']=( ($P[0]*100000.0) + ($P[1]*10000.0) + ($P[2]*1000.0) + ($P[3]*100.0) + ($P[4]*10.0) + ($P[5]*1.0) ) / 1000.0;    //вывод значения мощности в кВт/ч
    return $out;
}

//значения энергии
function get_values()
{
    $ret=$this->send_ret("27");
    if(!$ret)return false;
    $out[1]=$ret[5].$ret[6].$ret[7].".".$ret[8];
    $out[2]=$ret[9].$ret[10].$ret[11].".".$ret[12];
    $out[3]=$ret[13].$ret[14].$ret[15].".".$ret[16];
    $out[4]=$ret[17].$ret[18].$ret[19].".".$ret[20];
    //суммированные значения
    $out[0]=$out['1']+$out['2']+$out['3']+$out['4'];
    return $out;
}

//Удаляем лишнее из ответа
function decoderet($msg)
{
    $len=strlen($msg);
    $msg=str_split($msg,2);
    $len=count($msg);
    if($len<6){$error1="string too small($len <6)\n";return false;}
    if($msg[0].$msg[1].$msg[2].$msg[3]!=$this->addr){$error="line is busy\n";return false;}

    unset($msg[$len-1],$msg[$len-2],$msg[1],$msg[2],$msg[3],$msg[0],$msg[4]);
    return $msg;    
}

// Format input string as nice hex
function nice_hex($str) {
    return strtoupper(implode(' ',str_split($str,2)));
}

function crc16_modbus($msg)
{
    $data = pack('H*',$msg);
    $crc = 0xFFFF;
    for ($i = 0; $i < strlen($data); $i++)
    {
        $crc ^=ord($data[$i]);
        for ($j = 8; $j !=0; $j--)
        {
            if (($crc & 0x0001) !=0)
            {
                $crc >>= 1;
                $crc ^= 0xA001;
            }
            else $crc >>= 1;
        }
    }
    $crc=sprintf('%04X', $crc);
    return $crc[2].$crc[3].$crc[0].$crc[1];
}

}
$merc=new mercury206_lib("192.168.3.65","3020","12345678");

echo $error;
echo $error1;
if ($merc->get_serial() !="")
{
 echo "Serial number: ".$merc->get_serial()."<br>";
 echo "Battery level: ".$merc->get_battery()."<br>";
 $uip=$merc->get_uip();
 echo "U: ".$uip['U']." I: ".$uip['I']."A/ч"."(".$uip['mA']."mA/ч)"." P: ".$uip['P']."кВт/ч"."(".$uip['W']."Вт/ч)"."<br>";
 $values=$merc->get_values();
 echo "Power values: all: ".$values[0]." tarif1: ".$values[1]." tarif2: ".$values[2];
 sg('Sensor_voltage01.alive',1);
 sg('Sensor_power01.alive',1);
 sg('Counter03.alive',1);
 sg('Counter04.alive',1);
 sg('Counter05.alive',1);
 sg('Sensor_voltage01.value',$uip['U']);
 sg('Sensor_power01.value',$uip['P']*1000);
 sg('Counter03.value',$values['0']);
 sg('Counter04.value',$values['1']);
 sg('Counter05.value',$values['2']);
} else {
 echo "Msg is empty";
 sg('Sensor_voltage01.alive',0);
 sg('Sensor_power01.alive',0);
 sg('Counter03.alive',0);
 sg('Counter04.alive',0);
 sg('Counter05.alive',0);
}

Я создал сценарий mercury и дергаю его по крону раз в пять минут.
Конечно, вы должны в строке
$merc=new mercury206_lib("192.168.3.65","3020","12345678");
указать свой IP-адрес, порт ser2net и номер счетчика, также создать три счетчка-ПУ, ПУ-датчик мощности и ПУ-датчик напряжения, и направить в них данные из скрипта.
Если запустить сценарий вручную, мы должны получить вот такой ответ:

Serial number: 12345678
Battery level: 03.20
U: 230.7 I: 2.04A/ч(2040mA/ч) P: 0.46кВт/ч(460Вт/ч)
Power values: all: 13370.52 tarif1: 008934.61 tarif2: 004435.91

Если не получили, надо смотреть что там на устройстве не так.
Я изменил php-скрипт так, чтобы было видно, что счетчик не отдает данные (свойство alive=0) и чтобы в счетчики не сыпался всякий мусор и нули при ошибках.
Ну, и бонусом, опубликую еще один сценарий, который мне позволяет держать руку на пульсе расходов, потому что у меня электрокотел, который питается исключительно деньгами ))

$price_day = 6.39;
$price_night = 2.41;
$price = 5.56;
$days = date("j");
$days_month = date("t");
$count_day = gg('Counter04.valueMonth');
$count_night = gg('Counter05.valueMonth');
$summa_day = round($count_day*$price_day);
$summa_night = round($count_night*$price_night);
$count = $count_day+$count_night;
$summa = $summa_day+$summa_night;
sg('Counter06.value', $summa);
$summa_old = round(($count_day+$count_night)*$price);
$result = 
 '<pre>День: '.$count_day.' кВт/ч, '.$summa_day.' руб.'.chr(13).chr(10).
 'Ночь: '.$count_night.' кВт/ч, '.$summa_night.' руб.'.chr(13).chr(10).
 'Итого: '.$count.' кВт/ч, '.$summa.' руб., (по среднему: '.$summa_old.' руб.)'.chr(13).chr(10).
 'В среднем в день: '.round($count/$days).' кВт/ч, '.round($summa/$days).' руб.'.chr(13).chr(10).
 'Прогноз: '.round($count/$days)*$days_month.' кВт/ч, '.round($summa/$days*$days_month).' руб.</pre>';
echo $result;
return $result;

Данный скрипт я дергаю также раз в пять минут или из телеграм, а Counter06 - это еще один счетчик с деньгами.
В результате получается какой-то такой ответ

День: 123.78 кВт/ч, 791 руб.
Ночь: 112.55 кВт/ч, 271 руб.
Итого: 236.33 кВт/ч, 1062 руб., (по среднему: 1314 руб.)
В среднем в день: 30 кВт/ч, 133 руб.
Прогноз: 930 кВт/ч, 4115 руб.

после чего можно начинать паниковать, выключать котел и начинать топить печь.
Всем удачи, не болейте и пишите, если есть вопросы или нашли какие-то ошибки!

Discuss (1) (10)

See also:
2020-06-22 Снятие показаний со счетчика Меркурий 206 - часть 2

Москва, Россия