Raritan BCM 24-Kanal-Energiemessgerät

Hallo allerseits,

mit folgendem Script lässt sich ein Energiemessgerät vom Typ Raritan BCM (Branch Circuit Monitor) in IP-Symcon einbinden. Die BCM ist zum Einbau in die Elektro-Verteilung vorgesehen und überwacht den Spannung, Frequenz, Strom, Wirk- und Scheinleistung und Energieverbrauch von bis zu 21 Verbrauchern. Jeweils drei Kanäle können auch zusammengefasst werden, um einen dreiphasig angeschlossenen Verbraucher zu überwachen.

Die Kommunikation zwischen IP-Symcon und BCM erfolgt über Modbus/TCP. Das Script legt alle benötigten Instanzen selbständig an, es muss nur in den ersten Zeilen die IP-Adresse und die Kanalkonfiguration angepasst werden. In der BCM muss der Modbus-Dienst unter Device Services/Network/Modbus eingeschaltet werden.

Achtung: Das Script legt je nach Konfiguration bis zu 160 neue Variablen an (21 Kanäle mit je 7 Sensoren). Bitte vorher prüfen, ob diese Anzahl von der gewählten IP-Symcon-Lizenz unterstützt wird. Notfalls können nicht benötigte Sensoren auskommentiert werden.

Die BCM ist ab sofort zu Preisen zwischen 700,- und 1600,- Euro je nach Ausführung erhältlich. Eine Bezugsquelle im deutschen Elektro-Großhandel ist die Schlegel-Gruppe. Für weitere Bezugsquellen oder Rückfragen benutzen sie bitte die folgende E-Mail-Adresse: sales.germany@raritan.com

Datenblatt: http://partners.raritan.eu/resources/BCM_deutsch_Mai2013.pdf

Schöne Grüße
Ingo

PS: Dies ist mein erstes Script für IP-Symcon, konstruktive Kritik ist willkommen.

<?

# IP-Symcon configuration script for Raritan BCM branch circuit monitor
# Communication is handled via Modbus/TCP.
# Attention: This script will create >100 new variables; please make sure
# this is supported by your IP-Symcon license!

# Root category name for all created sensor instances
$name = 'BCM-2400';

# Host name/IP address and port number. Please make sure to enable the
# Modbus/TCP service in Device Settings/Services/Modbus.
$host = 'my-bcm.raritan.com';
$port = 502;

# Branch circuit configuration
# BCM-2400 has 7*3 branch circuit meters. Each set of meters can be used
# to measure either one three-phase branch or three single-phase branches.
$circuits = array(
	3, # Circuits 1, 2, 3:    three-phase branch
	3, # Circuits 4, 5, 6:    three-phase branch
	1, # Circuits 7, 8, 9:    three single-phase branches
	1, # Circuits 10, 11, 12: three single-phase branches
	3, # Circuits 13, 14, 15: three-phase branch
	1, # Circuits 16, 17, 18: three single-phase branches
	1, # Circuits 19, 20, 21: three single-phase branches
);

# Module GUIDs
define('MODBUS_RTU_TCP', '{A5F663AB-C400-4FE5-B207-4D67CC030564}');
define('MODBUS_ADDRESS', '{CB197E50-273D-4535-8C91-BB35273E3CA5}');
define('ARCHIVE_CONTROL', '{43192F0B-135B-4CE7-A0A7-1475603F3060}');

# Create missing variable profiles
function createProfiles() {
	$profiles = array(
		'RaritanVoltage' => array('type' => 2, 'unit' => 'V', 'digits' => 0),
		'RaritanCurrent' => array('type' => 2, 'unit' => 'A', 'digits' => 2),
		'RaritanCurrentUnbalance' => array('type' => 2, 'unit' => '%', 'digits' => 0),
		'RaritanActivePower' => array('type' => 2, 'unit' => 'W', 'digits' => 0),
		'RaritanApparentPower' => array('type' => 2, 'unit' => 'VA', 'digits' => 0),
		'RaritanPowerFactor' => array('type' => 2, 'unit' => '', 'digits' => 2),
		'RaritanActiveEnergy' => array('type' => 1, 'unit' => 'Wh', 'digits' => 0),
		'RaritanLineFrequency' => array('type' => 2, 'unit' => 'Hz', 'digits' => 1),
	);

	foreach ($profiles as $name => $settings) {
		if (!IPS_VariableProfileExists($name)) {
			if (!IPS_CreateVariableProfile($name, $settings['type']) ||
				!IPS_SetVariableProfileText($name, '', ' ' . $settings['unit']) ||
				!IPS_SetVariableProfileDigits($name, $settings['digits']))
			{
				trigger_error("Failed to create variable profile '$name'", E_USER_ERROR);
			}
		}
	}
}

# Create a new category
#   $name - Category name
#   $parent - Optional: Logical parent
#   $pos - Optional: Position in parent category
function createCategory($name, $parent = false, $pos = 0) {
	$cat = IPS_CreateCategory();
	IPS_SetName($cat, $name);
	if ($parent != false)
		IPS_SetParent($cat, $parent);
	IPS_SetPosition($cat, $pos);
	return $cat;
}

# Find the single instance of the Arcive Control module. This
# will raise an error in case fewer or more instances are found.
function getArchiveInstance() {
	static $archive;
	if (empty($archive)) {
		$archives = IPS_GetInstanceListByModuleID(ARCHIVE_CONTROL);
		if (count($archives) == 0)
			trigger_error('No Archive Control instance found', E_USER_ERROR);
		elseif (count($archives) > 1)
			trigger_error('More than one Archive Control instance found', E_USER_ERROR);
		$archive = $archives[0];
	}
	return $archive;
}

# Create Client Socket and ModBus RTU TCP instances
#   $host - Host name or IP address
#   $port - TCP port
#   $name - Name for ModBus RTU TCP instance
function createModbus($host, $port, $name) {
	$modbus = IPS_CreateInstance(MODBUS_RTU_TCP);
	IPS_SetName($modbus, "Modbus/TCP @ $name");
	IPS_SetProperty($modbus, 'SwapWords', 0);
	IPS_ApplyChanges($modbus);

	IPS_Sleep(500); # required for IP-Symcon 2.x where instances are connected asynchronously
	$socket = IPS_GetInstanceParentID($modbus);
	IPS_SetName($socket, "$host:$port");
	IPS_SetProperty($socket, 'Host', $host);
	IPS_SetProperty($socket, 'Port', $port);
	IPS_SetProperty($socket, 'Open', 1);
	IPS_ApplyChanges($socket);

	return $modbus;
}

# Create a new sensor instance
#   $category - Parent category
#   $pos - Position in parent category
#   $name - Name of new sensor instance
#   $modbus - ModBus RTU TCP instance
#   $address - Modbus register address
#   $type - Register type (3 = DWord, 7 = Real)
#   $profile - Variable profile name
function createSensor($category, $pos, $name, $modbus, $address, $type, $profile) {
	$sensor = IPS_CreateInstance(MODBUS_ADDRESS);
	IPS_SetName($sensor, $name);
	IPS_SetHidden($sensor, true);
	IPS_SetParent($sensor, $category);
	IPS_ConnectInstance($sensor, $modbus);
	Modbus_SetType($sensor, $type);
	IPS_SetProperty($sensor, 'ReadAddress', $address);
	IPS_ApplyChanges($sensor);

	$variable = IPS_GetVariableIDByName('Value', $sensor);
	IPS_SetVariableCustomProfile($variable, $profile);
	IPS_SetVariableCustomAction($variable, 1);

	$archive = getArchiveInstance();
	AC_SetLoggingStatus($archive, $variable, true);

	$link = IPS_CreateLink();
	IPS_SetName($link, $name);
	IPS_SetParent($link, $category);
	IPS_SetPosition($link, $pos);
	IPS_SetLinkChildID($link, $variable);
}

# Create category and sensor instances for Mains meter
#   $category - Parent category
#   $pos - Position in parent category
#   $modbus - ModBus RTU TCP instance
function createMains($category, $pos, $modbus) {
	$base = 0x3000;
	$cat = createCategory('Mains', $category, $pos);

	$pos = 0;
	createSensor($cat, $pos++, 'RMS Voltage', $modbus, $base + 0x08, 7, 'RaritanVoltage');
	createSensor($cat, $pos++, 'L1-L2 RMS Voltage', $modbus, $base + 0x48, 7, 'RaritanVoltage');
	createSensor($cat, $pos++, 'L2-L3 RMS Voltage', $modbus, $base + 0x78, 7, 'RaritanVoltage');
	createSensor($cat, $pos++, 'L3-L1 RMS Voltage', $modbus, $base + 0xa8, 7, 'RaritanVoltage');

	createSensor($cat, $pos++, 'RMS Current', $modbus, $base + 0x0a, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'L1 RMS Current', $modbus, $base + 0x4a, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'L2 RMS Current', $modbus, $base + 0x7a, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'L3 RMS Current', $modbus, $base + 0xaa, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'Current Unbalance', $modbus, $base + 0x10, 7, 'RaritanCurrentUnbalance');

    createSensor($cat, $pos++, 'Active Power', $modbus, $base + 0x12, 7, 'RaritanActivePower');
	createSensor($cat, $pos++, 'Apparent Power', $modbus, $base + 0x14, 7, 'RaritanApparentPower');
	createSensor($cat, $pos++, 'Power Factor', $modbus, $base + 0x16, 7, 'RaritanPowerFactor');
	createSensor($cat, $pos++, 'Active Energy', $modbus, $base + 0x1a, 3, 'RaritanActiveEnergy');
	createSensor($cat, $pos++, 'Line Frequency', $modbus, $base + 0x22, 7, 'RaritanLineFrequency');
}

# Create category and sensor instances for a single-phase branch circuit
#   $category - Parent category
#   $pos - Position in parent category
#   $modbus - ModBus RTU TCP instance
#   $branch - Branch number
#   $i - Outlet block index in Modbus register set
function create1PhaseBranch($category, $pos, $modbus, $branch, $i) {
	$name = "Branch $branch";
	$base = 0x8000 + $i * 0x100;
	$cat = createCategory($name, $category, $pos);

	$pos = 0;
	createSensor($cat, $pos++, 'RMS Voltage', $modbus, $base + 0x08, 7, 'RaritanVoltage');
	createSensor($cat, $pos++, 'RMS Current', $modbus, $base + 0x0a, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'Active Power', $modbus, $base + 0x12, 7, 'RaritanActivePower');
	createSensor($cat, $pos++, 'Apparent Power', $modbus, $base + 0x14, 7, 'RaritanApparentPower');
	createSensor($cat, $pos++, 'Power Factor', $modbus, $base + 0x16, 7, 'RaritanPowerFactor');
	createSensor($cat, $pos++, 'Active Energy', $modbus, $base + 0x1a, 3, 'RaritanActiveEnergy');
	createSensor($cat, $pos++, 'Line Frequency', $modbus, $base + 0x22, 7, 'RaritanLineFrequency');
}

# Create category and sensor instances for a three-phsae branch circuit
#   $category - Parent category
#   $pos - Position in parent category
#   $modbus - ModBus RTU TCP instance
#   $branch - Number of first branch channel
#   $i - Outlet block index in Modbus register set
function create3PhaseBranch($category, $pos, $modbus, $branch, $i) {
	$name = 'Branch ' . $branch . '-' . ($branch + 2);
	$base = 0x8000 + $i * 0x100;
	$cat = createCategory($name, $category, $pos);

	$pos = 0;
	createSensor($cat, $pos++, 'RMS Voltage', $modbus, $base + 0x08, 7, 'RaritanVoltage');
	createSensor($cat, $pos++, 'L1-L2 RMS Voltage', $modbus, $base + 0x48, 7, 'RaritanVoltage');
	createSensor($cat, $pos++, 'L2-L3 RMS Voltage', $modbus, $base + 0x78, 7, 'RaritanVoltage');
	createSensor($cat, $pos++, 'L3-L1 RMS Voltage', $modbus, $base + 0xa8, 7, 'RaritanVoltage');

	createSensor($cat, $pos++, 'RMS Current', $modbus, $base + 0x0a, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'L1 RMS Current', $modbus, $base + 0x4a, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'L2 RMS Current', $modbus, $base + 0x7a, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'L3 RMS Current', $modbus, $base + 0xaa, 7, 'RaritanCurrent');
	createSensor($cat, $pos++, 'Current Unbalance', $modbus, $base + 0x10, 7, 'RaritanCurrentUnbalance');

	createSensor($cat, $pos++, 'Active Power', $modbus, $base + 0x12, 7, 'RaritanActivePower');
	createSensor($cat, $pos++, 'Apparent Power', $modbus, $base + 0x14, 7, 'RaritanApparentPower');
	createSensor($cat, $pos++, 'Power Factor', $modbus, $base + 0x16, 7, 'RaritanPowerFactor');
	createSensor($cat, $pos++, 'Active Energy', $modbus, $base + 0x1a, 3, 'RaritanActiveEnergy');
	createSensor($cat, $pos++, 'Line Frequency', $modbus, $base + 0x22, 7, 'RaritanLineFrequency');
}

# Create categories and sensor instances for all branch circuits
#   $category - Parent category
#   $pos - Position in parent category
#   $modbus - ModBus RTU TCP instance
function createBranches($category, $pos, $modbus) {
	$outlet = 0;
	global $circuits;
	for ($i = 0; $i < count($circuits); $i++) {
	   $circuit_config = $circuits[$i];
		if ($circuit_config == 1) {
			create1PhaseBranch($category, $pos++, $modbus, 3*$i + 1, $outlet++);
			create1PhaseBranch($category, $pos++, $modbus, 3*$i + 2, $outlet++);
			create1PhaseBranch($category, $pos++, $modbus, 3*$i + 3, $outlet++);
		} elseif ($circuit_config == 3) {
			create3PhaseBranch($category, $pos++, $modbus, 3*$i + 1, $outlet++);
		} else {
			trigger_error("Invalid configuration for branch circuit $i", E_USER_ERROR);
		}
	}
}

# main function
createProfiles();
$cat_root = createCategory($name);
$modbus = createModbus($host, $port, $name);

createMains($cat_root, 0, $modbus);
createBranches($cat_root, 1, $modbus);

?>