diff --git a/Phpmodbus/ModbusMasterUdp.php b/Phpmodbus/ModbusMasterUdp.php index 8fb6635..78f3e01 100644 --- a/Phpmodbus/ModbusMasterUdp.php +++ b/Phpmodbus/ModbusMasterUdp.php @@ -37,6 +37,8 @@ class ModbusMasterUdp { var $sock; var $port = "502"; var $host = "192.168.1.1"; + var $client = ""; + var $client_port = "502"; var $status; var $timeout_sec = 5; // 5 sec var $endianess = 0; // defines endian codding (little endian == 0, big endian == 1) @@ -70,7 +72,17 @@ class ModbusMasterUdp { */ private function connect(){ // UDP socket - $this->sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + $this->sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + // Bind the client socket to a specific local port + if (strlen($this->client)>0){ + $result = socket_bind($this->sock, $this->client, $this->client_port); + if ($result === false) { + throw new Exception("socket_bind() failed.
Reason: ($result)". + socket_strerror(socket_last_error($this->sock))); + } else { + $this->status .= "Bound\n"; + } + } // connect $result = @socket_connect($this->sock, $this->host, $this->port); if ($result === false) { @@ -158,6 +170,121 @@ class ModbusMasterUdp { } } + /** + * readCoils + * + * Modbus function FC 1(0x01) - Read Coils + * + * Reads {@link $quantity} of Coils (boolean) from reference + * {@link $referenceRead} of a memory of a Modbus device given by + * {@link $unitId}. + * + * @param type $unitId + * @param type $reference + * @param type $quantity + */ + function readCoils($unitId, $reference, $quantity){ + $this->status .= "readCoils: START\n"; + // connect + $this->connect(); + // send FC 3 + $packet = $this->readCoilsPacketBuilder($unitId, $reference, $quantity); + $this->status .= $this->printPacket($packet); + $this->send($packet); + // receive response + $rpacket = $this->rec(); + $this->status .= $this->printPacket($rpacket); + // parse packet + $receivedData = $this->readCoilsParser($rpacket, $quantity); + // disconnect + $this->disconnect(); + $this->status .= "readCoils: DONE\n"; + // return + return $receivedData; + } + + /** + * fc1 + * + * Alias to {@link readMultipleCoils} method + * + * @param type $unitId + * @param type $reference + * @param type $quantity + * @return type + */ + function fc1($unitId, $reference, $quantity){ + return $this->readCoils($unitId, $reference, $quantity); + } + + /** + * readCoilsPacketBuilder + * + * FC1 packet builder - read coils + * + * @param type $unitId + * @param type $reference + * @param type $quantity + * @return type + */ + private function readCoilsPacketBuilder($unitId, $reference, $quantity){ + $dataLen = 0; + // build data section + $buffer1 = ""; + // build body + $buffer2 = ""; + $buffer2 .= iecType::iecBYTE(1); // FC 1 = 1(0x01) + // build body - read section + $buffer2 .= iecType::iecINT($reference); // refnumber = 12288 + $buffer2 .= iecType::iecINT($quantity); // quantity + $dataLen += 5; + // build header + $buffer3 = ''; + $buffer3 .= iecType::iecINT(rand(0,65000)); // transaction ID + $buffer3 .= iecType::iecINT(0); // protocol ID + $buffer3 .= iecType::iecINT($dataLen + 1); // lenght + $buffer3 .= iecType::iecBYTE($unitId); //unit ID + // return packet string + return $buffer3. $buffer2. $buffer1; + } + + /** + * readCoilsParser + * + * FC 1 response parser + * + * @param type $packet + * @param type $quantity + * @return type + */ + private function readCoilsParser($packet, $quantity){ + $data = array(); + // check Response code + $this->responseCode($packet); + // get data from stream + for($i=0;$i $quantity) continue; + // get boolean value + $v = ($value >> $i) & 0x01; + // build boolean array + if($v == 0){ + $data_bolean_array[] = FALSE; + } else { + $data_bolean_array[] = TRUE; + } + $di++; + } + } + return $data_bolean_array; + } + /** * readMultipleRegisters * @@ -205,8 +332,8 @@ class ModbusMasterUdp { */ function fc3($unitId, $reference, $quantity){ return $this->readMultipleRegisters($unitId, $reference, $quantity); - } - + } + /** * readMultipleRegistersPacketBuilder * @@ -257,6 +384,124 @@ class ModbusMasterUdp { return $data; } + /** + * writeMultipleCoils + * + * Modbus function FC15(0x0F) - Write Multiple Coils + * + * This function writes {@link $data} array at {@link $reference} position of + * memory of a Modbus device given by {@link $unitId}. + * + * @param type $unitId + * @param type $reference + * @param type $data + * @return type + */ + function writeMultipleCoils($unitId, $reference, $data){ + $this->status .= "writeMultipleCoils: START\n"; + // connect + $this->connect(); + // send FC16 + $packet = $this->writeMultipleCoilsPacketBuilder($unitId, $reference, $data); + $this->status .= $this->printPacket($packet); + $this->send($packet); + // receive response + $rpacket = $this->rec(); + $this->status .= $this->printPacket($rpacket); + // parse packet + $this->writeMultipleCoilsParser($rpacket); + // disconnect + $this->disconnect(); + $this->status .= "writeMultipleCoils: DONE\n"; + return true; + } + + /** + * fc15 + * + * Alias to {@link writeMultipleCoils} method + * + * @param int $unitId + * @param int $reference + * @param array $data + * @return bool + */ + function fc15($unitId, $reference, $data){ + return $this->writeMultipleCoils($unitId, $reference, $data); + } + + /** + * writeMultipleCoilsPacketBuilder + * + * Packet builder FC15 - Write multiple coils + * + * @param int $unitId + * @param int $reference + * @param array $data + * @return string + */ + private function writeMultipleCoilsPacketBuilder($unitId, $reference, $data){ + $dataLen = 0; + $endianness = 0; + // build bool stream to the WORD array + $data_word_stream = array(); + $data_word = 0; + $shift = 0; + for($i=0;$i 0)) { + $data_word_stream[] = $data_word; + $shift = 0; + $data_word = 0; + $data_word |= (0x01 && $data[$i]) << $shift; + $shift++; + } + else { + $data_word |= (0x01 && $data[$i]) << $shift; + $shift++; + } + } + $data_word_stream[] = $data_word; + // show binary stream to status string + foreach($data_word_stream as $d){ + $this->status .= sprintf("byte=b%08b\n", $d); + } + // build data section + $buffer1 = ""; + foreach($data_word_stream as $key=>$dataitem) { + $buffer1 .= iecType::iecBYTE($dataitem); // register values x + $dataLen += 1; + } + // build body + $buffer2 = ""; + $buffer2 .= iecType::iecBYTE(15); // FC 15 = 15(0x0f) + $buffer2 .= iecType::iecINT($reference); // refnumber = 12288 + $buffer2 .= iecType::iecINT(count($data)); // bit count + $buffer2 .= iecType::iecBYTE((count($data)+7)/8); // byte count + $dataLen += 6; + // build header + $buffer3 = ''; + $buffer3 .= iecType::iecINT(rand(0,65000)); // transaction ID + $buffer3 .= iecType::iecINT(0); // protocol ID + $buffer3 .= iecType::iecINT($dataLen + 1); // lenght + $buffer3 .= iecType::iecBYTE($unitId); // unit ID + + // return packet string + return $buffer3. $buffer2. $buffer1; + } + + /** + * writeMultipleCoilsParser + * + * FC15 response parser + * + * @param string $packet + * @return bool + */ + private function writeMultipleCoilsParser($packet){ + $this->responseCode($packet); + return true; + } + /** * writeMultipleRegister * @@ -321,7 +566,8 @@ class ModbusMasterUdp { * @return string */ private function writeMultipleRegisterPacketBuilder($unitId, $reference, $data, $dataTypes){ - $dataLen = 0; + $dataLen = 0; + $endianness = 0; // build data section $buffer1 = ""; foreach($data as $key=>$dataitem) { @@ -330,11 +576,11 @@ class ModbusMasterUdp { $dataLen += 2; } elseif($dataTypes[$key]=="DINT"){ - $buffer1 .= iecType::iecDINT($dataitem, $endianess); // register values x + $buffer1 .= iecType::iecDINT($dataitem, $endianness); // register values x $dataLen += 4; } elseif($dataTypes[$key]=="REAL") { - $buffer1 .= iecType::iecREAL($dataitem, $endianess); // register values x + $buffer1 .= iecType::iecREAL($dataitem, $endianness); // register values x $dataLen += 4; } else{ @@ -369,7 +615,7 @@ class ModbusMasterUdp { * @return bool */ private function writeMultipleRegisterParser($packet){ - $this->responseCode($rpacket); + $this->responseCode($packet); return true; } @@ -443,7 +689,8 @@ class ModbusMasterUdp { * @return string */ private function readWriteRegistersPacketBuilder($unitId, $referenceRead, $quantity, $referenceWrite, $data, $dataTypes){ - $dataLen = 0; + $dataLen = 0; + $endianness = 0; // build data section $buffer1 = ""; foreach($data as $key => $dataitem) { @@ -452,11 +699,11 @@ class ModbusMasterUdp { $dataLen += 2; } elseif($dataTypes[$key]=="DINT"){ - $buffer1 .= iecType::iecDINT($dataitem, $endianess); // register values x + $buffer1 .= iecType::iecDINT($dataitem, $endianness); // register values x $dataLen += 4; } elseif($dataTypes[$key]=="REAL") { - $buffer1 .= iecType::iecREAL($dataitem, $endianess); // register values x + $buffer1 .= iecType::iecREAL($dataitem, $endianness); // register values x $dataLen += 4; } else{ diff --git a/examples/example_750841_Mmemory.php b/examples/example_750841_Mmemory.php index d6909bd..2d153a6 100644 --- a/examples/example_750841_Mmemory.php +++ b/examples/example_750841_Mmemory.php @@ -3,7 +3,6 @@ require_once dirname(__FILE__) . '/../Phpmodbus/ModbusMasterUdp.php'; // Create Modbus object -// $ip = "192.168.1.1"; $ip = "192.192.15.51"; $modbus = new ModbusMasterUdp($ip); @@ -14,7 +13,6 @@ try { $mw0address = 12288; $quantity = 6; $recData = $modbus->readMultipleRegisters($moduleId, $reference, $quantity); - print_r($recData); } catch (Exception $e) { echo $modbus; diff --git a/examples/example_fc1.php b/examples/example_fc1.php new file mode 100644 index 0000000..7f74696 --- /dev/null +++ b/examples/example_fc1.php @@ -0,0 +1,25 @@ +readCoils(0, 12288, 12); +} +catch (Exception $e) { + // Print error information if any + echo $modbus; + echo $e; + exit; +} + +// Print status information +echo "
Status:
" . $modbus; + +// Print read data +echo "
Data:
"; +var_dump($recData); +echo "
"; \ No newline at end of file diff --git a/examples/example_fc15.php b/examples/example_fc15.php new file mode 100644 index 0000000..3b843fc --- /dev/null +++ b/examples/example_fc15.php @@ -0,0 +1,26 @@ +writeMultipleCoils(0, 12288, $data); +} +catch (Exception $e) { + // Print error information if any + echo $modbus; + echo $e; + exit; +} + +// Print status information +echo $modbus; diff --git a/examples/example_fc16.php b/examples/example_fc16.php index ddd3846..9f11112 100644 --- a/examples/example_fc16.php +++ b/examples/example_fc16.php @@ -6,8 +6,8 @@ require_once dirname(__FILE__) . '/../Phpmodbus/ModbusMasterUdp.php'; $modbus = new ModbusMasterUdp("192.192.15.51"); // Data to be writen -$data = array(1000, 2000, 3.0); -$dataTypes = array("INT", "DINT", "REAL"); +$data = array(10, -1000, 2000, 3.0); +$dataTypes = array("WORD", "INT", "DINT", "REAL"); try { // FC16 diff --git a/examples/example_fc23.php b/examples/example_fc23.php index 98cea54..2923f1c 100644 --- a/examples/example_fc23.php +++ b/examples/example_fc23.php @@ -6,8 +6,8 @@ require_once dirname(__FILE__) . '/../Phpmodbus/ModbusMasterUdp.php'; $modbus = new ModbusMasterUdp("192.192.15.51"); // Data to be writen -$data = array(1000, 2000, 3.0); -$dataTypes = array("INT", "DINT", "REAL"); +$data = array(10, -1000, 2000, 3.0); +$dataTypes = array("WORD", "INT", "DINT", "REAL"); try { // FC23 diff --git a/license.txt b/license.txt index 427e411..edea02a 100644 --- a/license.txt +++ b/license.txt @@ -1,7 +1,7 @@ The Phpmodbus License, Version 1 ============================ -Copyright (c) 2004, 2008 Jan Krakora, Wago (http://www.wago.com) +Copyright (c) 2004, 2011 Jan Krakora, Wago (http://www.wago.com) All rights reserved. This license is a legal agreement between you and Jan Krakora, Wago (the "Author") diff --git a/tests/IecType/ref/test.iecReal.php.html b/tests/IecType/ref/test.iecReal.php.html index 0fdc700..661237e 100644 --- a/tests/IecType/ref/test.iecReal.php.html +++ b/tests/IecType/ref/test.iecReal.php.html @@ -1 +1,12 @@ -Endianing off
0 --> Packet: 0000_0000_
1 --> Packet: 0000_3f80_
-2 --> Packet: 0000_c000_
0.333333333333 --> Packet: aaaa_3eaa_
25 --> Packet: 0000_41c8_
Endianing on
0 --> Packet: 0000_0000_
1 --> Packet: 3f80_0000_
-2 --> Packet: c000_0000_
0.333333333333 --> Packet: 3eaa_aaaa_
25 --> Packet: 41c8_0000_
\ No newline at end of file +Endianing off
+0 --> Packet: 0000_0000_
+1 --> Packet: 0000_3f80_
+-2 --> Packet: 0000_c000_
+0.333333333333 --> Packet: aaaa_3eaa_
+25 --> Packet: 0000_41c8_
+Endianing on
+0 --> Packet: 0000_0000_
+1 --> Packet: 3f80_0000_
+-2 --> Packet: c000_0000_
+0.333333333333 --> Packet: 3eaa_aaaa_
+25 --> Packet: 41c8_0000_
\ No newline at end of file diff --git a/tests/IecType/test.iecReal.php b/tests/IecType/test.iecReal.php index 3ba5aad..f9ed82d 100644 --- a/tests/IecType/test.iecReal.php +++ b/tests/IecType/test.iecReal.php @@ -25,26 +25,26 @@ function printPacket($packet){ if($i % 2) $str .= "_"; } - $str .= "
"; + $str .= "
\n"; return $str; } -echo "Endianing off
"; +echo "Endianing off
\n"; // Print mixed values for($i=0;$i "; $v = IecType::iecREAL($data[$i], 0); echo printPacket($v); - "
"; + "
\n"; } -echo "Endianing on
"; +echo "Endianing on
\n"; // Print mixed values for($i=0;$i "; $v = IecType::iecREAL($data[$i], 1); echo printPacket($v); - "
"; + "
\n"; } ?> \ No newline at end of file diff --git a/tests/ModbusMasterUdp/ref/test.fc16fc3bind.php.html b/tests/ModbusMasterUdp/ref/test.fc16fc3bind.php.html new file mode 100644 index 0000000..00dd046 --- /dev/null +++ b/tests/ModbusMasterUdp/ref/test.fc16fc3bind.php.html @@ -0,0 +1,72 @@ +Array +( + [0] => 0 + [1] => 0 + [2] => 0 + [3] => 1 + [4] => 0 + [5] => 1 + [6] => 0 + [7] => 255 + [8] => 0 + [9] => 255 +) +Array +( + [0] => 0 + [1] => 0 + [2] => 0 + [3] => 1 + [4] => 255 + [5] => 255 + [6] => 127 + [7] => 255 + [8] => 128 + [9] => 0 +) +Array +( + [0] => 0 + [1] => 0 + [2] => 0 + [3] => 0 + [4] => 0 + [5] => 1 + [6] => 0 + [7] => 0 + [8] => 255 + [9] => 255 + [10] => 255 + [11] => 255 + [12] => 255 + [13] => 255 + [14] => 127 + [15] => 255 + [16] => 0 + [17] => 0 + [18] => 128 + [19] => 0 +) +Array +( + [0] => 0 + [1] => 0 + [2] => 0 + [3] => 0 + [4] => 0 + [5] => 0 + [6] => 63 + [7] => 128 + [8] => 0 + [9] => 0 + [10] => 192 + [11] => 0 + [12] => 170 + [13] => 171 + [14] => 62 + [15] => 170 + [16] => 0 + [17] => 0 + [18] => 65 + [19] => 200 +) diff --git a/tests/ModbusMasterUdp/ref/test.fc26bind.php.html b/tests/ModbusMasterUdp/ref/test.fc26bind.php.html new file mode 100644 index 0000000..7991aa8 --- /dev/null +++ b/tests/ModbusMasterUdp/ref/test.fc26bind.php.html @@ -0,0 +1 @@ +writeMultipleRegister (FC26): DONE \ No newline at end of file diff --git a/tests/ModbusMasterUdp/test.fc15fc1.php b/tests/ModbusMasterUdp/test.fc15fc1.php new file mode 100644 index 0000000..5274123 --- /dev/null +++ b/tests/ModbusMasterUdp/test.fc15fc1.php @@ -0,0 +1,22 @@ +writeMultipleCoils(0, 12288, $data); +echo $modbus->status; +$modbus->status = ""; +echo "\n\n"; +// Read data - FC 1 +$recData = $modbus->readCoils(0, 12288, 32); +echo $modbus->status; +echo "\n\n"; +var_dump($recData); \ No newline at end of file diff --git a/tests/ModbusMasterUdp/test.fc16fc3bind.php b/tests/ModbusMasterUdp/test.fc16fc3bind.php new file mode 100644 index 0000000..c38edb4 --- /dev/null +++ b/tests/ModbusMasterUdp/test.fc16fc3bind.php @@ -0,0 +1,45 @@ +client = "192.192.15.133"; + +// Data to be writen - BYTE +$data = array(0, 1, 1, pow(2,8)-1, pow(2,8)-1); +$dataTypes = array("BYTE", "BYTE", "BYTE", "BYTE", "BYTE"); +// Write data - FC 16 +$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes); +// Read data - FC3 +$recData = $modbus->readMultipleRegisters(0, 12288, 5); +print_r($recData); + +// Data to be writen - INT +$data = array(0, 1, -1, pow(2,15)-1, -pow(2,15)); +$dataTypes = array("INT", "INT", "INT", "INT", "INT"); +// Write data - FC 16 +$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes); +// Read data - FC3 +$recData = $modbus->readMultipleRegisters(0, 12288, 5); +print_r($recData); + +// Data to be writen - DINT +$data = array(0, 1, -1, pow(2,31)-1, -pow(2,31)); +$dataTypes = array("DINT", "DINT", "DINT", "DINT", "DINT"); +// Write data - FC 16 +$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes); +// Read data - FC3 +$recData = $modbus->readMultipleRegisters(0, 12288, 10); +print_r($recData); + +// Data to be writen - REAL +$data = array(0, 1, -2, 1/3, 25); +$dataTypes = array("REAL", "REAL", "REAL", "REAL", "REAL"); +// Write data - FC 16 +$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes); +// Read data - FC3 +$recData = $modbus->readMultipleRegisters(0, 12288, 10); +print_r($recData); + +?> \ No newline at end of file diff --git a/tests/ModbusMasterUdp/test.fc26bind.php b/tests/ModbusMasterUdp/test.fc26bind.php new file mode 100644 index 0000000..8043f59 --- /dev/null +++ b/tests/ModbusMasterUdp/test.fc26bind.php @@ -0,0 +1,25 @@ +client = "192.192.15.133"; + +// Data to be writen +$data = array(1000, 2000, 1.250, 1.250); +$dataTypes = array("REAL", "REAL", "REAL", "REAL"); + +// FC23 +$recData = $modbus->readWriteRegisters(0, 12288, 6, 12288, $data, $dataTypes); + +if(!$recData) { + // Print error information if any + echo "
Error:
" . $modbus->errstr . "
"; + // + exit(); +} + +// Print status information +echo "writeMultipleRegister (FC26): DONE"; +?> \ No newline at end of file diff --git a/tutorials/Phpmodbus/Phpmodbus.pkg b/tutorials/Phpmodbus/Phpmodbus.pkg index 91b4e47..a1c5c09 100644 --- a/tutorials/Phpmodbus/Phpmodbus.pkg +++ b/tutorials/Phpmodbus/Phpmodbus.pkg @@ -21,7 +21,9 @@ The library implements: + FC 1: read multiple coils FC 3: read multiple registers + FC 15: write multiple coils FC 16: write multiple registers FC 23: read write registers @@ -44,25 +46,40 @@ Copy the Phpmodbus library to your PHP project folder. - Create a PHP script and assign the library using - - + Create a PHP script and assign the library using require_once() command + + - To create the Modbus master object, that will communicate with a Modbus slave - at IP address 192.168.1.1, write - + To create the Modbus master object communicating to the 192.168.1.1 modbus slave + at IP address 192.168.1.1 write + - + - To read 5 words (10 bytes) located at the memory beginning at address = 12288 of - the devide with Modbus ID=0 use - - readMultipleRegisters(0, 12288, 5); ]]> + To read 5 words (10 bytes) from the device ID=0 and its memory address 12288 + use try-catch method call + + readMultipleRegisters(0, 12288, 5); +} +catch (Exception $e) { + // Exception processing, e.g. print error information + echo $modbus; + echo $e; + exit; +}]]> + + To process the function byte stream, use conversion to PHP types in + + + + For other examples see sections bellow. @@ -72,6 +89,15 @@ This section presents library features and examples + + FC1 - read mutliple coils + + FC1 functionality example + + + {@example example_fc1.php} + + FC3 - read mutliple registers @@ -81,6 +107,15 @@ {@example example_fc3.php} + + FC15 - write mutliple coils + + FC15 functionality example + + + {@example example_fc15.php} + + FC16 - write mutliple registers