commit 6b55205c8ca37f61711dd4621937cb5fe32b1068 Author: John Long Date: Fri Jun 14 07:32:19 2013 -0500 0.2 diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..a7e5f6c --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +vendor +.svn diff --git a/Phpmodbus/IecType.php b/Phpmodbus/IecType.php new file mode 100644 index 0000000..cacca56 --- /dev/null +++ b/Phpmodbus/IecType.php @@ -0,0 +1,178 @@ +> 8) & 0x00FF) . + self::iecBYTE(($value & 0x00FF)); + } + + /** + * iecDINT + * + * Converts a value to IEC-1131 DINT data type + * + * @param value value to be converted + * @param value endianness defines endian codding (little endian == 0, big endian == 1) + * @return value IEC-1131 INT data type + * + */ + function iecDINT($value, $endianness = 0){ + // result with right endianness + return self::endianness($value, $endianness); + } + + /** + * iecREAL + * + * Converts a value to IEC-1131 REAL data type. The function uses function @use float2iecReal. + * + * @param value value to be converted + * @param value endianness defines endian codding (little endian == 0, big endian == 1) + * @return value IEC-1131 REAL data type + */ + function iecREAL($value, $endianness = 0){ + // iecREAL representation + $real = self::float2iecReal($value); + // result with right endianness + return self::endianness($real, $endianness); + } + + /** + * float2iecReal + * + * This function converts float value to IEC-1131 REAL single precision form. + * + * For more see [{@link http://en.wikipedia.org/wiki/Single_precision Single precision on Wiki}] or + * [{@link http://de.php.net/manual/en/function.base-convert.php PHP base_convert function commentary}, Todd Stokes @ Georgia Tech 21-Nov-2007]* + * + * @param float value to be converted + * @return value IEC REAL data type + */ + private function float2iecReal($value){ + $bias = 128; + $cnst = 281; // 1 (carry bit) + 127 + 1 + 126 + 24 + 2 (round bits) + $two_power_x = array(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, + 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, + 2097152, 4194304); + //convert and seperate input to integer and decimal parts + $val = abs($value); + $intpart = floor($val); + $decpart = $val - $intpart; + //convert integer part + for ($i=0;$i<$cnst;$i++) $real_significand_bin[$i] = 0; + $i = $bias; + while ((($intpart / 2) != 0) && ($i >= 0)) + { + $real_significand_bin[$i] = $intpart % 2; + if (($intpart % 2) == 0) $intpart = $intpart / 2; + else $intpart = $intpart / 2 - 0.5; + $i -= 1; + } + //convert decimal part + $i = $bias+1; + while (($decpart > 0) && ($i < $cnst)) + { + $decpart *= 2; + if ($decpart >= 1) { + $real_significand_bin[$i] = 1; + $decpart --; + $i++; + } + else + { + $real_significand_bin[i] = 0; + $i++; + } + } + //obtain exponent value + $i = 0; + //find most significant bit of significand + while (($i < $cnst) && ($real_significand_bin[$i] != 1)) $i++; + // + $index_exp = $i; + $real_exponent = 128 - $index_exp; + if ($real_exponent < -126) return 0; + if (($real_exponent > 127)&&($real_float>0)) return 0x7F7FFFFF; + if (($real_exponent > 127)&&($real_float<0)) return 0xFF7FFFFF; + for ($i=0; $i<23; $i++) + $real_significand = $real_significand + $real_significand_bin[$index_exp+1+$i] * $two_power_x[22-$i]; + // return + if ($value<0) $w = 0x80000000 + ($real_significand & 0x7FFFFF) + ((($real_exponent+127)<<23) & 0x7F800000); + else $w = ($real_significand & 0x7FFFFF) + ((($real_exponent+127)<<23) & 0x7F800000); + return $w; + } + + /** + * endianness + * + * Make endianess as required. + * For more see http://en.wikipedia.org/wiki/Endianness + * + * @param int $value + * @param bool $endianness + * @return int + */ + private function endianness($value, $endianness = 0){ + if ($endianness == 0) + return + self::iecBYTE(($value >> 8) & 0x000000FF) . + self::iecBYTE(($value & 0x000000FF)) . + self::iecBYTE(($value >> 24) & 0x000000FF) . + self::iecBYTE(($value >> 16) & 0x000000FF); + else + return + self::iecBYTE(($value >> 24) & 0x000000FF) . + self::iecBYTE(($value >> 16) & 0x000000FF) . + self::iecBYTE(($value >> 8) & 0x000000FF) . + self::iecBYTE(($value & 0x000000FF)); + } + +} + +?> \ No newline at end of file diff --git a/Phpmodbus/ModbusMasterUdp.php b/Phpmodbus/ModbusMasterUdp.php new file mode 100644 index 0000000..3681319 --- /dev/null +++ b/Phpmodbus/ModbusMasterUdp.php @@ -0,0 +1,557 @@ +host = $host; + } + + /** + * connect + * + * Connect the socket + * + * @return bool + */ + private function connect(){ + // UDP socket + $this->sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + // connect + $result = @socket_connect($this->sock, $this->host, $this->port); + if ($result === false) { + $this->errstr .= "socket_connect() failed.
Reason: ($result) " . + socket_strerror(socket_last_error($this->sock)); + return false; + } else { + $this->status .= "Connected
"; + return true; + } + } + + /** + * disconnect + * + * Disconnect the socket + */ + private function disconnect(){ + socket_close($this->sock); + $this->status .= "Disconnected
"; + } + + /** + * send + * + * Send the packet via Modbus + * + * @param string $packet + */ + private function send($packet){ + socket_write($this->sock, $packet, strlen($packet)); + $this->status .= "Send
"; + } + + /** + * rec + * + * Receive data from the socket + * + * @return bool + */ + private function rec(){ + socket_set_nonblock($this->sock); + $readsocks[] = $this->sock; + $writesocks = NULL; + $exceptsocks = NULL; + $rec = ""; + $lastAccess = time(); + while (socket_select($readsocks, + $writesocks, + $exceptsocks, + 0, + 300000) !== FALSE) { + $this->status .= "Wait received data
"; + if (in_array($this->sock, $readsocks)) { + while (@socket_recv($this->sock, $rec, 2000, 0)) { + $this->status .= "Received
"; + return $rec; + } + $lastAccess = time(); + } else { + if (time()-$lastAccess >= $this->timeout_sec) { + $this->errstr .= "Watchdog time expired [ " . + $this->timeout_sec . " sec]!!! Connection to " . + $this->host . " is not established."; + return false; + } + } + $readsocks[] = $this->sock; + } + } + + /** + * responseCode + * + * Check the Modbus response code + * + * @param string $packet + * @return bool + */ + private function responseCode($packet){ + if(($packet[7] & 0x80) > 0) { + $this->errstr .= "Modbus response error code:" . ord($packet[8]); + return false; + } + else + { + $this->status .= "Modbus response error code: NOERROR
"; + return true; + } + } + + /** + * readMultipleRegisters + * + * Modbus function FC 3(0x03) - Read Multiple Registers. + * + * This function reads {@link $quantity} of Words (2 bytes) from reference + * {@link $referenceRead} of a memory of a Modbus device given by + * {@link $unitId}. + * + * + * @param int $unitId usually ID of Modbus device + * @param int $reference Reference in the device memory to read data (e.g. in device WAGO 750-841, memory MW0 starts at address 12288). + * @param int $quantity Amounth of the data to be read from device. + * @return false|Array Success flag or array of received data. + */ + function readMultipleRegisters($unitId, $reference, $quantity){ + $this->errstr = ""; + $this->status = "readMultipleRegisters: START
"; + // connect + if(!$this->connect()) + return false; + // send FC 3 + $packet = $this->readMultipleRegistersPacketBuilder($unitId, $reference, $quantity); + $this->status .= $this->printPacket($packet); + $this->send($packet); + // receive response + $rpacket = $this->rec(); + if(!$rpacket) + return false; + $this->status .= $this->printPacket($rpacket); + // parse packet + $receivedData = $this->readMultipleRegistersParser($rpacket); + if(!$receivedData) + return false; + // disconnect + $this->disconnect(); + $this->status .= "readMultipleRegisters: DONE
"; + // return + return $receivedData; + } + + /** + * fc3 + * + * Alias to {@link readMultipleRegisters} method. + * + * @param int $unitId + * @param int $reference + * @param int $quantity + * @return false|Array + */ + function fc3($unitId, $reference, $quantity){ + return $this->readMultipleRegisters($unitId, $reference, $quantity); + } + + /** + * readMultipleRegistersPacketBuilder + * + * Packet FC 3 builder - read multiple registers + * + * @param int $unitId + * @param int $reference + * @param int $quantity + * @return string + */ + private function readMultipleRegistersPacketBuilder($unitId, $reference, $quantity){ + $dataLen = 0; + // build body + $buffer2 = ""; + $buffer2 .= iecType::iecBYTE(3); // FC 3 = 3(0x03) + // 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; + } + + /** + * readMultipleRegistersParser + * + * FC 3 response parser + * + * @param string $packet + * @return array + */ + private function readMultipleRegistersParser($packet){ + $data = array(); + // if not exception + if(!$this->responseCode($packet)) + return false; + // get data + for($i=0;$ierrstr = ""; + $this->status = "writeMultipleRegister: START
"; + // connect + if(!$this->connect()) + return false; + // send FC16 + $packet = $this->writeMultipleRegisterPacketBuilder($unitId, $reference, $data, $dataTypes); + $this->status .= $this->printPacket($packet); + $this->send($packet); + // receive response + $rpacket = $this->rec(); + if(!$rpacket) + return false; + $this->status .= $this->printPacket($rpacket); + // parse packet + if(!$this->writeMultipleRegisterParser($rpacket)) + return false; + // disconnect + $this->disconnect(); + $this->status .= "writeMultipleRegister: DONE
"; + return true; + } + + + /** + * fc16 + * + * Alias to {@link writeMultipleRegister} method + * + * @param int $unitId + * @param int $reference + * @param array $data + * @param array $dataTypes + * @return bool + */ + function fc16($unitId, $reference, $data, $dataTypes){ + return $this->writeMultipleRegister($unitId, $reference, $data, $dataTypes); + } + + + /** + * writeMultipleRegisterPacketBuilder + * + * Packet builder FC16 - WRITE multiple register + * e.g.: 4dd90000000d0010300000030603e807d00bb8 + * + * @param int $unitId + * @param int $reference + * @param array $data + * @param array $dataTypes + * @return string + */ + private function writeMultipleRegisterPacketBuilder($unitId, $reference, $data, $dataTypes){ + $dataLen = 0; + // build data section + $buffer1 = ""; + foreach($data as $key=>$dataitem) { + if($dataTypes[$key]=="INT"){ + $buffer1 .= iecType::iecINT($dataitem); // register values x + $dataLen += 2; + } + elseif($dataTypes[$key]=="DINT"){ + $buffer1 .= iecType::iecDINT($dataitem, $endianess); // register values x + $dataLen += 4; + } + elseif($dataTypes[$key]=="REAL") { + $buffer1 .= iecType::iecREAL($dataitem, $endianess); // register values x + $dataLen += 4; + } + else{ + $buffer1 .= iecType::iecINT($dataitem); // register values x + $dataLen += 2; + } + } + // build body + $buffer2 = ""; + $buffer2 .= iecType::iecBYTE(16); // FC 16 = 16(0x10) + $buffer2 .= iecType::iecINT($reference); // refnumber = 12288 + $buffer2 .= iecType::iecINT($dataLen/2); // word count + $buffer2 .= iecType::iecBYTE($dataLen); // 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; + } + + /** + * writeMultipleRegisterParser + * + * FC16 response parser + * + * @param string $packet + * @return bool + */ + private function writeMultipleRegisterParser($packet){ + if(!$this->responseCode($rpacket)) + return false; + return true; + } + + /** + * readWriteRegisters + * + * Modbus function FC23(0x17) - Read Write Registers. + * + * This function writes {@link $data} array at reference {@link $referenceWrite} + * position of memory of a Modbus device given by {@link $unitId}. Simultanously, + * it returns {@link $quantity} of Words (2 bytes) from reference {@link $referenceRead}. + * + * + * @param int $unitId usually ID of Modbus device + * @param int $referenceRead Reference in the device memory to read data (e.g. in device WAGO 750-841, memory MW0 starts at address 12288). + * @param int $quantity Amounth of the data to be read from device. + * @param int $referenceWrite Reference in the device memory to write data. + * @param array $data Array of values to be written. + * @param array $dataTypes Array of types of values to be written. The array should consists of string "INT", "DINT" and "REAL". + * @return false|Array Success flag or array of data. + */ + function readWriteRegisters($unitId, $referenceRead, $quantity, $referenceWrite, $data, $dataTypes){ + $this->errstr = ""; + $this->status = "readWriteRegisters: START
"; + // connect + if(!$this->connect()) + return false; + // send FC23 + $packet = $this->readWriteRegistersPacketBuilder($unitId, $referenceRead, $quantity, $referenceWrite, $data, $dataTypes); + $this->status .= $this->printPacket($packet); + $this->send($packet); + // receive response + $rpacket = $this->rec(); + if(!$rpacket) + return false; + $this->status .= $this->printPacket($rpacket); + // parse packet + $receivedData = $this->readWriteRegistersParser($rpacket); + if(!$receivedData) + return false; + // disconnect + $this->disconnect(); + $this->status .= "writeMultipleRegister: DONE
"; + // return + return $receivedData; + } + + /** + * fc23 + * + * Alias to {@link readWriteRegisters} method. + * + * @param int $unitId + * @param int $referenceRead + * @param int $quantity + * @param int $referenceWrite + * @param array $data + * @param array $dataTypes + * @return false|Array + */ + function fc23($unitId, $referenceRead, $quantity, $referenceWrite, $data, $dataTypes){ + return $this->readWriteRegisters($unitId, $referenceRead, $quantity, $referenceWrite, $data, $dataTypes); + } + + /** + * readWriteRegistersPacketBuilder + * + * Packet FC23 builder - READ WRITE registers + * + * + * @param int $unitId + * @param int $referenceRead + * @param int $quantity + * @param int $referenceWrite + * @param array $data + * @param array $dataTypes + * @return string + */ + private function readWriteRegistersPacketBuilder($unitId, $referenceRead, $quantity, $referenceWrite, $data, $dataTypes){ + $dataLen = 0; + // build data section + $buffer1 = ""; + foreach($data as $key => $dataitem) { + if($dataTypes[$key]=="INT"){ + $buffer1 .= iecType::iecINT($dataitem); // register values x + $dataLen += 2; + } + elseif($dataTypes[$key]=="DINT"){ + $buffer1 .= iecType::iecDINT($dataitem, $endianess); // register values x + $dataLen += 4; + } + elseif($dataTypes[$key]=="REAL") { + $buffer1 .= iecType::iecREAL($dataitem, $endianess); // register values x + $dataLen += 4; + } + else{ + $buffer1 .= iecType::iecINT($dataitem); // register values x + $dataLen += 2; + } + } + // build body + $buffer2 = ""; + $buffer2 .= iecType::iecBYTE(23); // FC 23 = 23(0x17) + // build body - read section + $buffer2 .= iecType::iecINT($referenceRead); // refnumber = 12288 + $buffer2 .= iecType::iecINT($quantity); // quantity + // build body - write section + $buffer2 .= iecType::iecINT($referenceWrite); // refnumber = 12288 + $buffer2 .= iecType::iecINT($dataLen/2); // word count + $buffer2 .= iecType::iecBYTE($dataLen); // byte count + $dataLen += 10; + // 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; + } + + + /** + * readWriteRegistersParser + * + * FC23 response parser + * + * @param string $packet + * @return array + */ + private function readWriteRegistersParser($packet){ + $data = array(); + // if not exception + if(!$this->responseCode($packet)) + return false; + // get data + for($i=0;$i> 4) & 0x0F); + $l = dechex($value & 0x0F); + return "$h$l"; + } + + /** + * printPacket + * + * Print whole packet in the hex form + * + * @param string $packet + * @return string + */ + private function printPacket($packet){ + $str = ""; + $str .= "Packet: "; + for($i=0;$ibyte2hex(ord($packet[$i])); + } + $str .= "
"; + return $str; + } +} + +?> \ No newline at end of file diff --git a/Phpmodbus/PhpType.php b/Phpmodbus/PhpType.php new file mode 100644 index 0000000..bdf8eea --- /dev/null +++ b/Phpmodbus/PhpType.php @@ -0,0 +1,219 @@ + 0)){ + $int = 0xFFFF8000 | $int; + } + // Convert the value + return (int) self::dword2signedInt($int); + } + + /** + * bytes2unsignedInt + * + * The function converts array of 2 or 4 bytes to unsigned integer. + * The return value depends on order of the input bytes (endianning). + * + * @param array $values + * @param bool $endianness + * @return int|float + */ + public static function bytes2unsignedInt($values, $endianness = 0){ + $data = array(); + $int = 0; + // Set the array to correct form + $data = self::checkData($values); + // Combine bytes + $int = self::combineBytes($data, $endianness); + // Convert the value + return self::dword2unsignedInt($int); + } + + /** + * real2float + * + * This function converts a value in IEC-1131 REAL single precision form to float. + * + * For more see [{@link http://en.wikipedia.org/wiki/Single_precision Single precision on Wiki}] or + * [{@link http://de.php.net/manual/en/function.base-convert.php PHP base_convert function commentary}, Todd Stokes @ Georgia Tech 21-Nov-2007] + * + * @param value value in IEC REAL data type to be converted + * @return float float value + */ + private static function real2float($value){ + $two_pow_minus_x = array( + 1, 0.5, 0.25, 0.125, 0.0625, 0.03125, 0.015625, + 0.0078125, 0.00390625, 0.001953125, 0.0009765625, + 0.00048828125, 0.000244140625, 0.0001220703125, + 0.00006103515625, 0.000030517578125, 0.0000152587890625, + 0.00000762939453125, 0.000003814697265625, 0.0000019073486328125, + 0.00000095367431640625, 0.000000476837158203125, + 0.0000002384185791015625, 0.00000011920928955078125); + // get sign, mantisa, exponent + $real_mantisa = $value & 0x7FFFFF | 0x800000; + $real_exponent = ($value>>23) & 0xFF; + $real_sign = ($value>>31) & 0x01; + $bin_exponent = $real_exponent - 127; + // decode value + if (( $bin_exponent >= -126) && ($bin_exponent <= 127)) { + // Mantissa decoding + for ($i=0; $i<24; $i++) { + if ($real_mantisa & 0x01) + $val += $two_pow_minus_x[23-$i]; + $real_mantisa = $real_mantisa >> 1; + } + // Base + $val = $val * pow(2,$bin_exponent); + if (($real_sign == 1)) $val = -$val; + } + return (float)$val; + } + + /** + * dword2signedInt + * + * Switch double word to signed integer + * + * @param int $value + * @return int + */ + private static function dword2signedInt($value){ + if ((0x80000000 & $value) != 0) { + return -(0x7FFFFFFF & ~$value)-1; + } else { + return (0x7FFFFFFF & $value); + } + } + + /** + * dword2signedInt + * + * Switch double word to unsigned integer + * + * @param int $value + * @return int|float + */ + private static function dword2unsignedInt($value){ + if ((0x80000000 & $value) != 0) { + return ((float) (0x7FFFFFFF & $value)) + 2147483648; + } else { + return (int) (0x7FFFFFFF & $value); + } + } + + /** + * checkData + * + * Check if the data variable is array, and check if the values are numeric + * + * @param int $data + * @return int + */ + private static function checkData($data){ + // Check the data + if (!is_array($data)) { + throw new Exception('The input data should be an array of bytes.'); + } + // Check the values to be number - must be + if (!is_numeric($data[0]) || !is_numeric($data[1])) { + throw new Exception('Data are not numeric.'); + } + if (!is_numeric($data[2])) $data[2] = 0; + if (!is_numeric($data[3])) $data[3] = 0; + + return $data; + } + + /** + * combineBytes + * + * Combine bytes together + * + * @param int $data + * @param bool $endianness + * @return int + */ + private static function combineBytes($data, $endianness){ + $value = 0; + // Combine bytes + if ($endianness == 0) + $value = (($data[3] & 0xFF)<<16) | + (($data[2] & 0xFF)<<24) | + (($data[1] & 0xFF)) | + (($data[0] & 0xFF)<<8); + else + $value = (($data[3] & 0xFF)<<24) | + (($data[2] & 0xFF)<<16) | + (($data[1] & 0xFF)<<8) | + (($data[0] & 0xFF)); + + return $value; + } +} +?> \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..668b139 --- /dev/null +++ b/composer.json @@ -0,0 +1,17 @@ +{ + "name": "adduc/phpmodbus", + "description": "Composer version of PhpModBus", + "license": "LGPL", + "authors": [ + { + "name": "Honza Krakora", + "email": "krakora.jan@googlemail.com" + } + ], + "autoload": { + "classmap": [ "Phpmodbus" ] + }, + "require": { + "ext-sockets": "*" + } +} diff --git a/examples/example_750841_Mmemory.php b/examples/example_750841_Mmemory.php new file mode 100644 index 0000000..c9a418e --- /dev/null +++ b/examples/example_750841_Mmemory.php @@ -0,0 +1,73 @@ +readMultipleRegisters($moduleId, $reference, $quantity); + +?> + + + + + WAGO 750-841 M-memory dump + + +

Dump of M-memory from WAGO 750-84x series coupler.

+
    +
  • PLC: 750-84x series
  • +
  • IP:
  • +
  • Modbus module ID:
  • +
  • Modbus memory reference:
  • +
  • Modbus memory quantity:
  • +
+ +

M-memory dump

+ + Error:
" . $modbus->errstr . "
"; + } + else + { + ?> + + + + + + + + + + + + + + +
Modbus addressMWxvalue
MW
+ + + +

Modbus class status

+ status; + ?> + + + diff --git a/examples/example_datatype.php b/examples/example_datatype.php new file mode 100644 index 0000000..996c518 --- /dev/null +++ b/examples/example_datatype.php @@ -0,0 +1,55 @@ +readMultipleRegisters(0, 12288, 10); + +if(!$recData) { + // Print error information if any + echo "
Error:
" . $modbus->errstr . "
"; + exit; +} + +// Received data +echo "

Received Data

"; +print_r($recData); + +// Conversion +echo "

32 bits types

"; +// Chunk the data array to set of 4 bytes +$values = array_chunk($recData, 4); + +// Get float from REAL interpretation +echo "

REAL to Float

"; +foreach($values as $bytes) + echo PhpType::bytes2float($bytes) . "
"; + +// Get integer from DINT interpretation +echo "

DINT to integer

"; +foreach($values as $bytes) + echo PhpType::bytes2signedInt($bytes) . "
"; + +// Get integer of float from DINT interpretation +echo "

DWORD to integer (or float)

"; +foreach($values as $bytes) + echo PhpType::bytes2unsignedInt($bytes) . "
"; + +echo "

16 bit types

"; +// Chunk the data array to set of 4 bytes +$values = array_chunk($recData, 2); + +// Get signed integer from INT interpretation +echo "

INT to integer

"; +foreach($values as $bytes) + echo PhpType::bytes2signedInt($bytes) . "
"; + +// Get unsigned integer from WORD interpretation +echo "

WORD to integer

"; +foreach($values as $bytes) + echo PhpType::bytes2unsignedInt($bytes) . "
"; +?> \ No newline at end of file diff --git a/examples/example_fc16.php b/examples/example_fc16.php new file mode 100644 index 0000000..3d74f0c --- /dev/null +++ b/examples/example_fc16.php @@ -0,0 +1,21 @@ +writeMultipleRegister(0, 12288, $data, $dataTypes)) { + // Print error information if any + echo "
Error:
" . $modbus->errstr . "
"; +} + +// Print status information +echo "
Status:
" . $modbus->status . "
"; + +?> \ No newline at end of file diff --git a/examples/example_fc23.php b/examples/example_fc23.php new file mode 100644 index 0000000..0c37992 --- /dev/null +++ b/examples/example_fc23.php @@ -0,0 +1,26 @@ +readWriteRegisters(0, 12288, 6, 12288, $data, $dataTypes); + +if(!$recData) { + // Print error information if any + echo "
Error:
" . $modbus->errstr . "
"; +} + +// Print status information +echo "
Status:
" . $modbus->status . "
"; + +// Print read data +echo "
Data:
"; print_r($recData); echo "
"; + +?> \ No newline at end of file diff --git a/examples/example_fc3.php b/examples/example_fc3.php new file mode 100644 index 0000000..568ded1 --- /dev/null +++ b/examples/example_fc3.php @@ -0,0 +1,21 @@ +readMultipleRegisters(0, 12288, 6); + +if(!$recData) { + // Print error information if any + echo "
Error:
" . $modbus->errstr . "
"; +} + +// Print status information +echo "
Status:
" . $modbus->status . "
"; + +// Print read data +echo "
Data:
"; print_r($recData); echo "
"; +?> \ No newline at end of file diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..427e411 --- /dev/null +++ b/license.txt @@ -0,0 +1,62 @@ +The Phpmodbus License, Version 1 +============================ + +Copyright (c) 2004, 2008 Jan Krakora, Wago (http://www.wago.com) +All rights reserved. + +This license is a legal agreement between you and Jan Krakora, Wago (the "Author") +for the use of Phpmodbus (the "Software"). By obtaining, using and/or +copying the Software, you agree that you have read, understood, and will +comply with the terms and conditions of this license. + + +PERMITTED USE +------------- + +You are permitted to use, copy, modify, and distribute the Software and its +documentation, with or without modification, for any purpose, provided that +the following conditions are met: + +1. A copy of this license agreement must be included with the distribution. + +2. Redistributions of source code must retain the above copyright notice in + all source code files. + +3. Redistributions in binary form must reproduce the above copyright notice + in the documentation and/or other materials provided with the distribution. + +4. Products derived from the Software must include an acknowledgment that + they are derived from "Phpmodbus" in their documentation and/or other + materials provided with the distribution. + +5. The name "Phpmodbus" must not be used to endorse or promote products + derived from the Software without prior written permission from Author. + +6. Products derived from the Software may not be called "Phpmodbus", + nor may "Phpmodbus" appear in their name, without prior written + permission from Author. + + +INDEMNITY +--------- + +You agree to indemnify and hold harmless the Author and any contributors +for any direct, indirect, incidental, or consequential third-party claims, +actions or suits, as well as any related expenses, liabilities, damages, +settlements or fees arising from your use or misuse of the Software, +or a violation of any terms of this license. + + +DISCLAIMER OF WARRANTY +---------------------- + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..dc308b3 --- /dev/null +++ b/readme.txt @@ -0,0 +1,19 @@ +Copyright (c) 2004, 2009 Jan Krakora, Wago (http://www.wago.com) +All rights reserved. + +Phpmodbus library +#################### + +Phpmodbus for PHP is a small and easy-to-use Modbus UDP master library. For more +see project at http://phpmodbus.googlecode.com + +Release notes +=============== + +0.1 -> 0.2.r20 +--------------- ++ Added new class for conversion from received bytes to PHP data types (PhpType class) ++ Added new data conversion using PhpType example ++ Added new alias methods fc3, fc16 and fc23 (ModbusMasterUdp class) +* Fixed problems with the endianess when data written (IecType class) +* Improved commentaries for documentation diff --git a/tests/IecType/_test.bat b/tests/IecType/_test.bat new file mode 100644 index 0000000..8122004 --- /dev/null +++ b/tests/IecType/_test.bat @@ -0,0 +1,11 @@ +@echo off +call ../config.bat + +for %%f in (test.*.php) do %php% -q "%%f" > "output/%%f.html" + +cd output +for %%f in (*.html) do %diff% "%%f" ../ref/"%%f" +cd .. +pause + +@echo on \ No newline at end of file diff --git a/tests/IecType/ref/test.iecByte.php.html b/tests/IecType/ref/test.iecByte.php.html new file mode 100644 index 0000000..dc6e8a7 --- /dev/null +++ b/tests/IecType/ref/test.iecByte.php.html @@ -0,0 +1 @@ +125
98
0
0
0
0
0
0
0
0
0
0
255
255
255
255
158
88
97
168
\ No newline at end of file diff --git a/tests/IecType/ref/test.iecDInt.php.html b/tests/IecType/ref/test.iecDInt.php.html new file mode 100644 index 0000000..1abf31b --- /dev/null +++ b/tests/IecType/ref/test.iecDInt.php.html @@ -0,0 +1 @@ +Endianing off
0 --> Packet: 0000_0000_
1 --> Packet: 0001_0000_
-1 --> Packet: ffff_ffff_
2147483647 --> Packet: ffff_7fff_
-2147483648 --> Packet: 0000_8000_
Endianing on
0 --> Packet: 0000_0000_
1 --> Packet: 0000_0001_
-1 --> Packet: ffff_ffff_
2147483647 --> Packet: 7fff_ffff_
-2147483648 --> Packet: 8000_0000_
\ No newline at end of file diff --git a/tests/IecType/ref/test.iecInt.php.html b/tests/IecType/ref/test.iecInt.php.html new file mode 100644 index 0000000..583ad9b --- /dev/null +++ b/tests/IecType/ref/test.iecInt.php.html @@ -0,0 +1 @@ +Endianing off
0 --> Packet: 0000_
1 --> Packet: 0001_
-1 --> Packet: ffff_
32767 --> Packet: 7fff_
-32768 --> Packet: 8000_
Endianing on
0 --> Packet: 0000_
1 --> Packet: 0001_
-1 --> Packet: ffff_
32767 --> Packet: 7fff_
-32768 --> Packet: 8000_
\ No newline at end of file diff --git a/tests/IecType/ref/test.iecReal.php.html b/tests/IecType/ref/test.iecReal.php.html new file mode 100644 index 0000000..0fdc700 --- /dev/null +++ b/tests/IecType/ref/test.iecReal.php.html @@ -0,0 +1 @@ +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.iecByte.php b/tests/IecType/test.iecByte.php new file mode 100644 index 0000000..8af17c8 --- /dev/null +++ b/tests/IecType/test.iecByte.php @@ -0,0 +1,33 @@ + 125, // 32098 (DINT) + "1" => 98, + "2" => 0, + "3" => 0, + "4" => 0, // 0 (DINT) + "5" => 0, + "6" => 0, + "7" => 0, + "8" => 0, // 0 (DINT) + "9" => 0, + "10" => 0, + "11" => 0, + "12" => 255, // -1 (DINT) + "13" => 255, + "14" => 255, + "15" => 255, + "16" => 158, // -25000 (INT) + "17" => 88, + "18" => 97, // 25000 (INT) + "19" => 168 +); + +// Print mixed values +foreach($data as $d) + echo ord(IecType::iecBYTE($d)) . "
"; + +?> \ No newline at end of file diff --git a/tests/IecType/test.iecDInt.php b/tests/IecType/test.iecDInt.php new file mode 100644 index 0000000..24a4820 --- /dev/null +++ b/tests/IecType/test.iecDInt.php @@ -0,0 +1,50 @@ + 0, + "1" => 1, + "2" => -1, + "3" => pow(2,31)-1, + "4" => -pow(2,31) +); + +function byte2hex($value){ + $h = dechex(($value >> 4) & 0x0F); + $l = dechex($value & 0x0F); + return "$h$l"; +} + +function printPacket($packet){ + $str = ""; + $str .= "Packet: "; + for($i=0;$i"; +// Print mixed values +for($i=0;$i "; + $v = IecType::iecDINT($data[$i], 0); + echo printPacket($v); + "
"; +} + +echo "Endianing on
"; +// Print mixed values +for($i=0;$i "; + $v = IecType::iecDINT($data[$i], 1); + echo printPacket($v); + "
"; +} + +?> \ No newline at end of file diff --git a/tests/IecType/test.iecInt.php b/tests/IecType/test.iecInt.php new file mode 100644 index 0000000..dec812e --- /dev/null +++ b/tests/IecType/test.iecInt.php @@ -0,0 +1,50 @@ + 0, + "1" => 1, + "2" => -1, + "3" => pow(2,15)-1, + "4" => -pow(2,15) +); + +function byte2hex($value){ + $h = dechex(($value >> 4) & 0x0F); + $l = dechex($value & 0x0F); + return "$h$l"; +} + +function printPacket($packet){ + $str = ""; + $str .= "Packet: "; + for($i=0;$i"; +// Print mixed values +for($i=0;$i "; + $v = IecType::iecINT($data[$i], 0); + echo printPacket($v); + "
"; +} + +echo "Endianing on
"; +// Print mixed values +for($i=0;$i "; + $v = IecType::iecINT($data[$i], 1); + echo printPacket($v); + "
"; +} + +?> \ No newline at end of file diff --git a/tests/IecType/test.iecReal.php b/tests/IecType/test.iecReal.php new file mode 100644 index 0000000..3ba5aad --- /dev/null +++ b/tests/IecType/test.iecReal.php @@ -0,0 +1,50 @@ + 0, // -> 0000 0000 + "1" => 1, // -> 3f80 0000 + "2" => -2, // -> c000 0000 + "3" => 1/3, // -> 3eaa aaab + "4" => 25 // -> 41c8 0000 +); + +function byte2hex($value){ + $h = dechex(($value >> 4) & 0x0F); + $l = dechex($value & 0x0F); + return "$h$l"; +} + +function printPacket($packet){ + $str = ""; + $str .= "Packet: "; + for($i=0;$i"; +// Print mixed values +for($i=0;$i "; + $v = IecType::iecREAL($data[$i], 0); + echo printPacket($v); + "
"; +} + +echo "Endianing on
"; +// Print mixed values +for($i=0;$i "; + $v = IecType::iecREAL($data[$i], 1); + echo printPacket($v); + "
"; +} + +?> \ No newline at end of file diff --git a/tests/ModbusMasterUdp/_test.bat b/tests/ModbusMasterUdp/_test.bat new file mode 100644 index 0000000..8122004 --- /dev/null +++ b/tests/ModbusMasterUdp/_test.bat @@ -0,0 +1,11 @@ +@echo off +call ../config.bat + +for %%f in (test.*.php) do %php% -q "%%f" > "output/%%f.html" + +cd output +for %%f in (*.html) do %diff% "%%f" ../ref/"%%f" +cd .. +pause + +@echo on \ No newline at end of file diff --git a/tests/ModbusMasterUdp/ref/test.fc16fc3.php.html b/tests/ModbusMasterUdp/ref/test.fc16fc3.php.html new file mode 100644 index 0000000..c3e4836 --- /dev/null +++ b/tests/ModbusMasterUdp/ref/test.fc16fc3.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] => 170 + [14] => 62 + [15] => 170 + [16] => 0 + [17] => 0 + [18] => 65 + [19] => 200 +) diff --git a/tests/ModbusMasterUdp/ref/test.fc26.php.html b/tests/ModbusMasterUdp/ref/test.fc26.php.html new file mode 100644 index 0000000..7991aa8 --- /dev/null +++ b/tests/ModbusMasterUdp/ref/test.fc26.php.html @@ -0,0 +1 @@ +writeMultipleRegister (FC26): DONE \ No newline at end of file diff --git a/tests/ModbusMasterUdp/test.fc16fc3.php b/tests/ModbusMasterUdp/test.fc16fc3.php new file mode 100644 index 0000000..fe0c565 --- /dev/null +++ b/tests/ModbusMasterUdp/test.fc16fc3.php @@ -0,0 +1,44 @@ +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.fc26.php b/tests/ModbusMasterUdp/test.fc26.php new file mode 100644 index 0000000..ac5ec6d --- /dev/null +++ b/tests/ModbusMasterUdp/test.fc26.php @@ -0,0 +1,24 @@ +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/tests/PhpType/_test.bat b/tests/PhpType/_test.bat new file mode 100644 index 0000000..8122004 --- /dev/null +++ b/tests/PhpType/_test.bat @@ -0,0 +1,11 @@ +@echo off +call ../config.bat + +for %%f in (test.*.php) do %php% -q "%%f" > "output/%%f.html" + +cd output +for %%f in (*.html) do %diff% "%%f" ../ref/"%%f" +cd .. +pause + +@echo on \ No newline at end of file diff --git a/tests/PhpType/ref/test.bytes2mixed.php.html b/tests/PhpType/ref/test.bytes2mixed.php.html new file mode 100644 index 0000000..952bc9d --- /dev/null +++ b/tests/PhpType/ref/test.bytes2mixed.php.html @@ -0,0 +1 @@ +32098
0
0
-1
-25000
25000
\ No newline at end of file diff --git a/tests/PhpType/ref/test.bytes2real.php.html b/tests/PhpType/ref/test.bytes2real.php.html new file mode 100644 index 0000000..7d44edb --- /dev/null +++ b/tests/PhpType/ref/test.bytes2real.php.html @@ -0,0 +1 @@ +1000
2000
1.25
\ No newline at end of file diff --git a/tests/PhpType/ref/test.bytes2signedint.php.html b/tests/PhpType/ref/test.bytes2signedint.php.html new file mode 100644 index 0000000..a2b10ea --- /dev/null +++ b/tests/PhpType/ref/test.bytes2signedint.php.html @@ -0,0 +1 @@ +-1
0
1
-2147483648
2147483647
diff --git a/tests/PhpType/ref/test.bytes2unsignedint.php.html b/tests/PhpType/ref/test.bytes2unsignedint.php.html new file mode 100644 index 0000000..29be8ee --- /dev/null +++ b/tests/PhpType/ref/test.bytes2unsignedint.php.html @@ -0,0 +1,6 @@ +float(4294967295) +
int(0) +
int(1) +
float(2147483648) +
int(2147483647) +
diff --git a/tests/PhpType/test.bytes2mixed.php b/tests/PhpType/test.bytes2mixed.php new file mode 100644 index 0000000..6b1b391 --- /dev/null +++ b/tests/PhpType/test.bytes2mixed.php @@ -0,0 +1,37 @@ + 125, // 32098 (DINT) + "1" => 98, + "2" => 0, + "3" => 0, + "4" => 0, // 0 (DINT) + "5" => 0, + "6" => 0, + "7" => 0, + "8" => 0, // 0 (DINT) + "9" => 0, + "10" => 0, + "11" => 0, + "12" => 255, // -1 (DINT) + "13" => 255, + "14" => 255, + "15" => 255, + "16" => 158, // -25000 (INT) + "17" => 88, + "18" => 97, // 25000 (INT) + "19" => 168 +); + +// Print mixed values +echo PhpType::bytes2unsignedInt(array_slice($data, 0, 4)) . "
"; +echo PhpType::bytes2signedInt(array_slice($data, 4, 4)) . "
"; +echo PhpType::bytes2signedInt(array_slice($data, 8, 4)) . "
"; +echo PhpType::bytes2signedInt(array_slice($data, 12, 4)) . "
"; +echo PhpType::bytes2signedInt(array_slice($data, 16, 2)) . "
"; +echo PhpType::bytes2signedInt(array_slice($data, 18, 2)) . "
"; + +?> \ No newline at end of file diff --git a/tests/PhpType/test.bytes2real.php b/tests/PhpType/test.bytes2real.php new file mode 100644 index 0000000..b6acd56 --- /dev/null +++ b/tests/PhpType/test.bytes2real.php @@ -0,0 +1,27 @@ + 0, + 1 => 0, + 2 => 68, + 3 => 122, + 4 => 0, + 5 => 0, + 6 => 68, + 7 => 250, + 8 => 0, + 9 => 0, + 10 => 63, + 11 => 160, +); + +$dword = array_chunk($data, 4); + +// Print float interpretation of the real value +echo PhpType::bytes2float($dword[0]) . "
"; +echo PhpType::bytes2float($dword[1]) . "
"; +echo PhpType::bytes2float($dword[2]) . "
"; +?> \ No newline at end of file diff --git a/tests/PhpType/test.bytes2signedint.php b/tests/PhpType/test.bytes2signedint.php new file mode 100644 index 0000000..567299c --- /dev/null +++ b/tests/PhpType/test.bytes2signedint.php @@ -0,0 +1,35 @@ +"; +} +?> diff --git a/tests/PhpType/test.bytes2unsignedint.php b/tests/PhpType/test.bytes2unsignedint.php new file mode 100644 index 0000000..9aa6f37 --- /dev/null +++ b/tests/PhpType/test.bytes2unsignedint.php @@ -0,0 +1,36 @@ +"; +} +?> diff --git a/tests/config.bat b/tests/config.bat new file mode 100644 index 0000000..d62ddfe --- /dev/null +++ b/tests/config.bat @@ -0,0 +1,5 @@ +set php=C:\Programme\xampplite\php\php.exe +rem set php=C:\PHP\versions\php-5.2.6-Win32\php.exe -d auto_prepend_file=C:\PHP\locale.php +set diff="C:\Program Files\Octave\msys\bin\diff.exe" +set testUri=http://localHost/nette/_trunk/tests +set wget=wget.exe diff --git a/tests/config.php b/tests/config.php new file mode 100644 index 0000000..f94dfc6 --- /dev/null +++ b/tests/config.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/tutorials/Phpmodbus/Phpmodbus.pkg b/tutorials/Phpmodbus/Phpmodbus.pkg new file mode 100644 index 0000000..5ece7f3 --- /dev/null +++ b/tutorials/Phpmodbus/Phpmodbus.pkg @@ -0,0 +1,121 @@ + + + Phpmodbus user's guide + Phpmodbus How-to + + + + Jan Krakora + + {@link mailto:jak.krakora@wago.com email} + + + + {@toc} + + Introduction + + Phpmodbus is a PHP library for the Modbus protocol. The library implements + Modbus UDP master class with subset of the most used Modbus commands. + + + The library implements: + + FC 3: read multiple registers + FC 16: write multiple registers + FC 23: read write registers + + + + For more about Modbus protocol see [{@link http://www.modbus.org}] or + [{@link http://en.wikipedia.org/wiki/Modbus Wiki}] + + + Developed with support of {@link http://www.wago.com}. + + + + Installation + + At the first, it is supposed an PHP solution has been already installed on + your server (LAMP, WAMP, MSS+CGI etc.). + + + Copy the Phpmodbus library to your PHP project folder. + + + Create a PHP script and assign the library using + + + + + + To create the Modbus master object, that will communicate with a 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); ]]> + + + + For other examples see sections bellow. + + + + Examples + + This section presents library features and examples + + + FC3 - read mutliple registers + + FC3 functionality example + + + {@example example_fc3.php} + + + + FC16 - write mutliple registers + + FC16 functionality example + + + {@example example_fc16.php} + + + + FC23 - read write registers + + FC23 functionality example + + + {@example example_fc23.php} + + + + Dump of M-memory from WAGO 750-84x series coupler. + + Dump of M-memory from WAGO 750-84x series coupler. + + + {@example example_750841_Mmemory.php} + + + + Data conversion to PHP types. + + Conversion of the data bytes, received from Modbus, to a PHP type. + + + {@example example_datatype.php} + + + + \ No newline at end of file diff --git a/tutorials/Phpmodbus/wago_logo.png b/tutorials/Phpmodbus/wago_logo.png new file mode 100644 index 0000000..681b064 Binary files /dev/null and b/tutorials/Phpmodbus/wago_logo.png differ