テクセル

PowerShellでPLCシリアル通信(三菱iQ-R,Qシリーズ)


powershellスクリプトで三菱電機(株)製PLC(シーケンサ)iQ-R,Qシリーズとシリアル通信を行います。実行環境は、以下となります。

項目 内容
対象としたシリアルポート シリアルコミュニケーションユニット:RJ71C24,RJ71C24-R2,QJ71C24N,QJ71C24N-R2 QCPUのシリアルポート(RS-232C)
通信手順  MCプロトコル、QnA互換3Cフレーム形式4、サムチェックあり
通信仕様  通信速度9600bps、データ長8ビット、1ストップビット、奇数パリティ

□ PLCでの設定

1.シリアルコミュニケーションユニット RJ71C24,RJ71C24-R2 での設定例

GX Works3のナビゲーションウインドウのプロジェクトから[パラメータ]-[ユニット情報]-[****:RJ71C24]の「ユニットパラメータ」を マウスダブルクリックし設定シートを表示します。(RJ71C24は、ユニットパラメータに登録されているものとします。) CH1に設定します。交信プロトコル「MCプロトコル(形式4)」、通信速度「9600bps」、動作設定「独立」、データビット「8」、 パリティビット「あり」、奇数/偶数パリティ「奇数」、ストップビット「1」、サムチェックコード「あり」、RUN中書込み「許可」 と設定します。

2.シリアルコミュニケーションユニット QJ71C24N,QJ71C24N-R2 での設定例

GX Works2のナビゲーションウインドウのプロジェクトからインテリジェント機能ユニット「QJ71C24N-R2」の「スイッチ設定」を マウスダブルクリックしスイッチ設定ダイアログを表示します。(QJ71C24N-R2は、インテリジェント機能ユニットに登録されている ものとします。)
CH1に設定します。データビット「8」、パリティビット「あり」、奇数/偶数パリティ「奇数」、ストップビット「1」、 サムチェックコード「あり」、RUN中書込み「許可」、通信速度「9600bps」、交信プロトコル「MCプロトコル(形式4)」 と設定します。

3.QシリーズCPUのRS-232Cシリアルポートでの設定例

GX Works2のナビゲーションウインドウのプロジェクトから PCパラメータ マウスダブルクリックでQパラメータ設定ダイアログを表示します。 シリアルコミュニケーション設定ダブを選択します。
「シリアルコミュニケーション機能を使用する」にチェックを入れます。伝送速度を「9.6kBPS」、サムチェック「あり」にチェックを入れ、 伝送ウエイト時間を「ウエイト無し」、RUN中書込設定を「許可する」にチェックを入れます。

□ 通信プログラムの例

下記ソフトは、レジスタのD0100より10ワード読み込みを行ってます。受信時のサムチェック及び その他のエラー処理は行ってません。「Q」キーで終了します。
プログラム実行は、コンソールウインドウで実行して下さい。ISEでは、エラーとなります。
iQ-Rではコマンドが追加されています。(サブコマンド) 追加されたコマンドではデバイスコードが4バイド、デバイス番号が8バイトとなっています。下記プログラムでは、変数 $iQRExMode で iQ-R 追加コマンド使用ありなしを指定しています。


# C24MC_3C4.pas1
# 三菱PLC シリアル通信 MCプロトコル
# QnA互換3Cフレーム形式4

Set-StrictMode -Version latest

# キー入力
function Keyin{

   $rtkey = $null
   if ([Console]::KeyAvailable){
      $rtkey = [Console]::ReadKey($true)
   }
   return $rtkey
}

# 受信処理       受信データセット byte列    受信文字長
function receive([ref][byte[]]$byRcvDt, [ref][int]$irlen){

   $ir = 0
   $il = $Script:compt.BytesToRead     # 受信バッファ文字数
   if ($il -gt 0){
      [byte[]] $brbuf = New-Object byte[] $il
      $ic = $Script:compt.Read($brbuf, 0, $il)  # 受信データ読み込み 戻り値:読み込み文字数
      for($ip = 0; $ip -lt $ic; $ip++){
         $ba = $brbuf[$ip]
         if ($irlen.Value -eq 0){      # 受信文字長 0
            if ($ba -eq 0x02 -or $ba -eq 0x15){ # 先頭文字 STX, NAK ?
               $byRcvDt.Value[0] = $ba
               $irlen.Value++
            }
         }
         else{
            $byRcvDt.Value[$irlen.Value] = $ba
            $irlen.Value++
            if ($ba -eq 0x0a){         # 末尾文字 LF ? 
               $ir = 1                 # 受信完了
               break;
            }
         }
      }
   }
   return $ir
}

# サム値計算    サム値計算用 byte列、  サム値セット byte列
function sumCal([byte[]]$bar, [ref][byte[]]$bysum){

   $isum = 0
   foreach($ia in $bar){
      $isum += $ia
   }
   $shex = $isum.ToString("X4")   # hex string 長さ4
   $bysum.Value[0] = [byte]$shex[2]
   $bysum.Value[1] = [byte]$shex[3]
}

# *******
$iQRExMode = 0       # 1:iQ-R追加モード

# Dデバイス読み込み --------
if ($iQRExMode -eq 0){  # 通常
   $srsbcmd = "0000"    # サブコマンド
   $sdv = "D*"          # Dデバイス(デバイスコード)
   $dvketa = "D6"       # デバイス桁
}
else{    # iQ-R追加拡張
   $srsbcmd = "0002"    # サブコマンド
   $sdv = "D***"        # Dデバイス(デバイスコード)
   $dvketa = "D8"       # デバイス桁
}
$itopad = 100           # 読み込み先頭
$ireadsu = 10           # 読み込み個数

# Mデバイス読み込み -------
<# if ($iQRExMode -eq 0){ # 通常
   $srsbcmd = "0001"    # サブコマンド
   $sdv = "M*"          # Mデバイス(デバイスコード)
   $dvketa = "D6"       # デバイス桁
}
else{    # iQ-R追加拡張
   $srsbcmd = "0003"    # サブコマンド
   $sdv = "M***"        # Mデバイス(デバイスコード)
   $dvketa = "D8"       # デバイス桁
}
$itopad = 100           # 読み込み先頭
$ireadsu = 20           # 読み込み個数
#>
# ------------------------
if ($iQRExMode -eq 0){
   Write-Host "通常モードで実行"
}
else{
   Write-Host "iQ-R追加拡張モードで実行"
}

[byte[]] $bENQ = @(0x05)          # ENQ
[byte[]] $bCRLF = @(0x0d, 0x0a)   # CRLF

[byte[]]$basum = New-Object byte[] 2   # サム値セット byte列
[byte[]]$brcv = New-Object byte[] 1024 # 受信データセットエリア byte列

$ircvLen = 0        # 受信データ長
$irstep = 1         # 処理ステップ
$srcmd = "0401"     # 読み込みコマンド

# comポート 3 通信速度 9600bps
$Script:compt = New-Object System.IO.Ports.SerialPort "COM3", 9600 # Script スコープ
$Script:compt.DataBits = 8        # データ長8ビット
$Script:compt.Parity = [System.IO.Ports.Parity]::Odd     # 奇数パリティ
$Script:compt.StopBits = [System.IO.Ports.StopBits]::One # 1stopbit
$Script:compt.DtrEnable = $true   # DTR有効
$Script:compt.RtsEnable = $true   # RTS有効

try{
   $Script:compt.Open()  # comポート open
}
catch{
   Write-Host("COMポートOpen失敗")
   exit
}
Write-Host("COMポートopen")

$ssa = "F90000FF00" + $srcmd + $srsbcmd + $sdv + $itopad.ToString($dvketa) + $ireadsu.ToString("X4")
[byte[]] $baa = [System.Text.Encoding]::ASCII.GetBytes($ssa)  # string -> byte列
sumCal $baa ([ref]$basum)       # サム値計算
[byte[]] $baSend = $bENQ + $baa + $basum + $bCRLF   # 送信データ

while($true){

   switch($irstep){
      1{
         $Script:compt.Write($baSend, 0, $baSend.Length)     # 送信
         $irstep = 2        # 受信待ち
         $ircvLen = 0       # 受信文字長
      }
      2{  # 受信待ち
         $ir = receive ([ref]$brcv) ([ref]$ircvLen)
         if ($ir -eq 1){    # 受信完了?
            $srd = [System.Text.Encoding]::ASCII.GetString($brcv, 0, $ircvLen)  # byte列 -> string 変換
            $ik = 0
            if ($sdv.substring(0, 1) -eq "D"){$ik = 4}
            if ($sdv.substring(0, 1) -eq "M"){$ik = 1}
            $sv = [string]::Empty
            for($ip = 0; $ip -lt $ireadsu; $ip++){
               $sv += $srd.Substring(11 + $ip * $ik, $ik) + ","
            }
            Write-Host $sv
            $irstep = 1    # 送信
         }
      }
   }
   $inkey = Keyin
   if ($inkey -ne $null -and $inkey.Key -eq "Q"){
      break;
   }
   Start-Sleep -m 50
}
$Script:compt.Close()
Write-Host("COMポートclose")
         
参考資料
・MELSECコミュニケーションプロトコルリファレンスマニュアル(三菱電機(株))
PowerShellスクリプト