M5StackのSTAMPLCから、三菱電機FX5UCのデバイスをMCプロトコルで読み書きします。
接続にはFX5UC本体内蔵のRS-485ポートを使用します。最初はD100を読み取ってLCDに表示するだけの構成から始め、ボタンでの値変更や折れ線グラフの表示まで、同じ配線で試せるようにしました。
MCプロトコルにはmcprotocol-serial-cpp、STAMPLC本体・LCD・ボタン・RGB LEDにはM5Stack公式ライブラリを使います。
ソースコードは以下で公開しています。
使用するもの
| 項目 | 使用するもの |
|---|---|
| M5Stack | M5Stack STAMPLC |
| PLC | 三菱電機FX5U/FX5UC(本体内蔵RS-485) |
| 開発環境 | VS Code / PlatformIO、GX Works3 |
| 配線 | RS-485用ツイストペア線、信号GND線 |
STAMPLCの仕様と端子配置はM5Stack公式製品ページを参照してください。
1. RS-485を配線する
STAMPLCのPWR-485とFX5UCの内蔵RS-485端子を接続します。今回は2線式の半二重通信なので、FX5UC側では送信・受信の同極性端子を短絡します。
FX5U/FX5UCにのみ内蔵RS-485端子はあります。FX5S,FX5UJにはありません。
電源を切ってから配線してください。
PWR-485の電源+を、FX5UCのSGや通信信号端子へ接続しないでください。
| STAMPLC PWR-485 | FX5U/FX5UC |
|---|---|
| A | SDAとRDAを短絡して接続 |
| B | SDBとRDBを短絡して接続 |
| GND | SG |
1対1接続では両端の終端抵抗を有効にします。
赤枠がSTAMPLC側の終端抵抗です。右でON

FX5UCは下面に終端抵抗SWがあります。

2. GX Works3で通信条件を設定する
FX5UCPUのユニットパラメータから、485シリアルポートを設定します。
| 項目 | 設定値 |
|---|---|
| プロトコル | MCプロトコル |
| 伝文形式 | 形式5 |
| ボーレート | 19200bps |
| データ長 | 8bit |
| パリティ | 偶数 |
| ストップビット | 1bit |
| サムチェック | 付加する |
| 局番 | 0 |
設定したパラメータをPLCへ書き込み、CPUリセットで反映します。
STAMPLC側のSERIAL_8E1は、8bit・偶数パリティ・ストップビット1bitを表します。
GX Works3のプロジェクトもGX_Works3フォルダに格納しています。使用前に、接続するPLCの構成とパラメータを確認してください。
3. PlatformIOのプロジェクトを開く
リポジトリ内の、使用するデモのフォルダをPlatformIOで開きます。最初はSTAMPLC_D100_SIMPLEです。
各フォルダにplatformio.iniがあり、ライブラリはPlatformIOが取得します。ビルドと転送はPlatformIOの操作で行えます。
| ライブラリ | 使用バージョン |
|---|---|
| mcprotocol-serial-cpp | 4.2.1 |
| M5StamPLC | 1.2.0 |
| M5Unified | 0.2.13 |
| M5GFX | 0.2.28 |
PlatformIOのプラットフォームはespressif32@6.12.0、ボード指定はesp32-s3-devkitc-1です。STAMPLCの本体初期化はM5StamPLCが行います。
4. D100を読み取ってLCDに表示する
シンプル版は、読み取り関数の中で応答を待つ同期方式です。
処理の流れは「D100を読む → 結果を表示する → 1秒待つ」の繰り返しです。以下はソースの要点を抜き出したものです。プログラム全体はSTAMPLC_D100_SIMPLEにあります。
UART1をMC通信用に使う
Esp32UartClient plc(1);
STAMPLCのPWR-485はUART1に接続されています。UARTの初期化・送受信はライブラリに任せるので、同じポートに対してSerial1.begin()を追加する必要はありません。
M5StamPLCのModbusスレーブ機能も同じUARTを使用するため、今回は無効にします。
auto config = M5StamPLC.config();
config.enableModbusSlave = false;
M5StamPLC.config(config);
M5StamPLC.begin();
形式5と送受信方向を指定する
const auto protocol = ProtocolConfig::c4_binary(
PlcProfile::MelsecIqF, SumCheckMode::Enabled,
RouteConfig{HostStationRoute{}}, TimeoutConfig{3000, 250});
c4_binary()が4Cフレームのバイナリ、つまり今回使用する形式5です。MelsecIqFでFX5U向けのプロファイル、Enabledでサムチェックありを指定します。
RS-485の送受信方向はUARTドライバに切り替えてもらいます。
uart.baud = 19200;
uart.format = SERIAL_8E1;
uart.rx_pin = STAMPLC_PIN_485_RX;
uart.tx_pin = STAMPLC_PIN_485_TX;
uart.direction = Esp32Direction::Rs485Rts;
uart.rts_pin = STAMPLC_PIN_485_DIR;
RX・TX・DIRにはM5公式ライブラリのピン定義を使います。
符号付き整数として受け取る
読み取りの中心は次の呼び出しです。
int16_t d100 = 0;
if (status.ok()) status = plc.read_word({DeviceCode::D, 100}, d100);
ここでのstatusは、その前に行ったUART初期化・復旧の結果です。成功している場合だけ読み取りを開始します。
read_word()は応答またはタイムアウトまで待ちます。受け取り先をint16_tにすることで、D100を−32768~32767の符号付き整数として扱えます。アプリ側で符号変換を書く必要はありません。
成功した値は、M5公式ライブラリでLCDに表示します。
lcd.printf("D100:%d", static_cast<int>(d100));
LCDには次のように表示します。
D100:1234
デバイス名と数値は同じ3倍サイズです。通信異常時は古い値を消し、COMM Errorを表示します。
5. 通信待ち中も操作するなら非同期版
同期版は処理を追いやすい一方、応答待ちやdelay()の間は同じloop内のLCD・ボタン更新も止まります。
STAMPLC_D100_MONITORは非同期版です。要求を開始した後はloopに戻り、plc.update()で通信処理を進めます。
check(plc.async_read_word({DeviceCode::D, 100}, d100, onRead));
読み取り完了時にonRead()が呼ばれます。要求を受け付けたことと、PLCから正常に読めたことは別なので、完了時の結果も確認します。
受信先のd100は完了まで残っている必要があるため、非同期版ではグローバル変数にしています。追加のRTOSタスクは使用していません。
6. D100の変化を折れ線グラフにする
STAMPLC_D100_TRENDでは、読み取った値をLCDにグラフ表示します。
| 表示・操作 | 内容 |
|---|---|
| 上段 | 現在のD100 |
| 中段 | 直近120回の値を折れ線表示 |
| 下段 | 最小値・最大値・サンプル数 |
| KEY1 | 履歴を消去 |
グラフ枠は横240×縦78ピクセルです。右が新しい値で、履歴が満杯になると古い値から捨てます。縦軸は表示中の最小値・最大値に合わせて自動調整します。
取得間隔は、読み取り結果の表示後に200ms待つ設定です。固定の200ms周期ではなく、通信・描画の時間も加わります。120点分の表示期間は、おおよそ24秒に処理時間を加えた長さです。
通信異常中は最後のグラフを保持し、失敗した読み取りは追加しません。横軸は成功したサンプルの順序なので、通信が途切れた時間は横幅に反映されません。
RGB LEDと通信異常時の動作
RGB LEDは通信の結果を表示します。
| 色 | 意味 |
|---|---|
| 緑 | 読み取り成功。 |
| 赤 | 通信異常。 |


