M5StackでModbus通信を使用した4チャンネルのリレーユニットの使い方を紹介します。
Modbus通信用の2本の配線だけで4つのリレーのON/OFFを制御できるため、出力点数の限られたデバイスでPLC(Programmable Logic Controller、シーケンサとも言う)のような制御や有線での多点遠隔操作をしたい場合に最適です。
今回使用したリレーユニットには「入力端子」も4点あるため、これ単体で入力4点、出力4点の制御ができます。
さらに増設すれば、数十点〜数百点の入出力を制御することも可能になります。
今回は基本的な入力状態の確認から、リレー出力の方法、増設するためのスレーブアドレスの指定方法等を詳しく紹介していきます。
動作確認には「M5Stack Basic Ver2.6」を使用しました。
「M5Stack Basic」の使い方や開発環境の準備については以下のリンクで詳しく紹介しています。
Modbus通信を使用した温湿度計「XY-MD02」の使い方は、以下のリンクで詳しく紹介しています。
1.Modbusとは
2.リレーユニット 485 Relay 4CHについて
・基本仕様
・外観、端子配列
・通信仕様一覧表
3.通信方法の確認準備
・準備するもの
・配線図
・使用ライブラリ
4.基本的な通信方法の確認
・動作紹介
・通信確認用サンプルプログラム(コピペ)
・初期設定詳細
・スレーブアドレスの設定方法(保持レジスタ連続書込み:0x10)
・スレーブアドレスの確認方法(保持レジスタ読込:0x03)
・リレーのON/OFF状態確認方法(コイルON/OFF状態読込み:0x01)
・入力端子のON/OFF状態確認方法(入力ステータス読込み:0x02)
・リレーのON/OFF方法(コイルON/OFF単独書込み:0x05)
・全リレーのON/OFF方法(コイルON/OFF連続書込み:0x0F)
5.まとめ
1.Modbusとは
「Modbus」とは、通信プロトコルの1つで、主に産業機器(PLC、センサー、モーターなど)の制御に使用されます。
マスタースレーブ方式で動作し、1つのマスターに対して複数のスレーブ機器を制御できます。
プロトコルはシリアル通信や、TCP/IP上のEthernet通信(LAN、WAN)でも使用でき、通信に必要な配線は2本(A+、B-)だけです。
通信距離はシリアル通信(RS485)では数百m〜1km程、TCP / IPではLANで100m程(中継可)WANならネットワーク環境によって広範囲の通信が可能です。
比較的簡単に実装でき柔軟性があるため、工場やプロセスオートメーション、エネルギー管理などの分野で広く使用されています。
2.リレーユニット 485 Relay 4CHについて
今回使用したリレーユニット「485 Relay 4CH」についての情報を以下にまとめました。
・基本仕様
基本仕様は下表のようになります。
項目 | 内容 |
---|---|
製品名 | Modbus RTU 4チャンネル12Vリレー出力ボードモジュールスイッチ入力RS485 |
型式 | 485 Relay 4CH V1.1 |
コントローラー | STM社製 8S003F3P6 |
通信プロトコル | Modbus-RTU |
通信速度(ボーレート) | 9600bps(8bit、パリティ無し[NONE]、ストップビット1bit) |
通信可能距離 | 1000m(RS485) |
通信(スレーブ)アドレス | 1〜247(初期値1)※ブロードキャスト0 |
Modbus通信端子 | A+, B- |
入力端子 | IN1, IN2, IN3, IN4, GNDx2 |
リレー | リレー1〜4(C接点 NO/NC) |
動作電圧 | DC 5V〜12V(アダプター入力、プラグ外径φ5.5) |
サイズ(W x H x D) | H67 x W76 x (D19) mm |
・外観、端子配列
今回使用したリレーユニット「485 Relay 4CH」の外観は下画像のようになります。
電源入力はACアダプター(プラグ外径φ5.5)が使用でき、リレー用出力端子はC接点(NO/NC)でコネクタになっているため、抜き差しが可能です。
電源は端子台でも入力可能で、他にも入力用の端子台と、Modbus通信用の端子「 A+, B−」があります。
・通信仕様一覧表
通信仕様については下表のようになります。
Modbus通信では「ファンクションコード」によって通信内容が異なります。
「ファンクションコード」はいくつかありますが、このリレーユニットでは以下表のものが使用できます。
スレーブアドレスの初期値は「1」ですが、変更することも可能です。
変更するためにはスレーブアドレスにブロードキャストの「0」を指定して行います。
今回使用したリレーユニットの場合は、スレーブアドレス「0」で、他にも以下表のようにバージョン確認等ができます。
以下表はリレーのON/OFF制御や入出力端子の状態を確認する方法をまとめたものです。
リレーの制御には「コイル」の読み書きで行います。
入力端子の状態確認には「入力ステータス」を読み込んで行います。
3.通信方法の確認準備
リレーユニットの動作確認をするために使用した部品や配線図、ライブラリについては以下のようになります。
・準備するもの
動作確認に必要なものは以下になります。
・M5Stack Basic(Core2でもシリアル通信端子を合わせれば使用可能です。)
※2023/6/17時点では「yahoo!ショッピング」が6千円台で一番安かったです。
・RS485シリアル通信モジュール
UARTとRS485の変換モジュールであればなんでも良いです。
私はAmazonで買える以下のものを使ってますが「Yahoo!ショッピング」で買える「M5Stack用RS485ユニット(GROVEコネクタタイプ)」も扱いやすいです。
・リレーユニット 485 Relay 4CH
ここで紹介したものはちょっと前に購入したもので同じものはAmazonではありませんでしたので以下同等品になります。基本的なリレーのON/OFF方法は同じですが保持レジスタの内容は違う可能性あります。
・配線図
配線図は下画像のようになります。
M5StackのUART(RX/TX)端子とRS485の変換モジュールは、使用するものに合わせて配線してください。
「M5Stack用RS485ユニット」を使用した場合の配線図は下画像になります。(アイキャッチ画像の配線です。)
・使用ライブラリ
使用したライブラリは「ModbusMaster」です。
Modbus通信用のライブラリはたくさんありますが「ArduinoIDE」のライブラリマネージャで検索した場合は、下画像の「Doc Walker」さんのライブラリです。
お使いの開発環境で検索してインストールしてください。
4.基本的な通信方法の確認
サンプルプログラムを使用して、基本的な通信方法の確認を行っていきます。
・動作紹介
通信確認用サンプルプログラムを書き込んで実行したものは下画像のようになります。
液晶画面の上から「スレーブアドレス番号」「入力端子(IN)1〜4のON/OFF状態」「リレー1〜4のON/OFF状態」が表示されます。
4つのスイッチは入力端子IN1〜4に接続され「IN1」のスイッチを押すと「リレー1」がONし「IN2」のスイッチを押すと「リレー2」がONのように各リレーがONするようにプログラムされています。
「ボタンA」を押すと、スレーブアドレスが「2」に変更されて動作が継続できます。
「ボタンB」を押すと、スレーブアドレスが「3」に変更されて動作が継続できます。
「ボタンC」を押すと、全てのリレーがONし、離すと全てOFFします。
・通信確認用サンプルプログラム(コピペ)
通信確認用のサンプルプログラムを以下に準備しました。
各通信の動作は関数としてまとめてますので、それぞれの関数ごとに詳しく紹介していきます。
サンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでもコピーできます。
#include <M5Stack.h> // CORE2の場合は <M5Core2.h>
#include "ModbusMaster.h" // modbusライブラリのインクルード
#define RX_PIN 16 // シリアル通信端子番号(RX)※CORE2は13
#define TX_PIN 17 // シリアル通信端子番号(TX)※CORE2は14
#define SLAVE_NO 1 // スレーブアドレス指定
ModbusMaster node0; // スレーブアドレス読み書き専用の通信用インスタンスを作成
ModbusMaster node1; // 指定したスレーブアドレスとの通信用インスタンスを作成
// 変数宣言
uint8_t result = 0; // 通信結果取得用
uint8_t read_slave_address; // スレーブアドレス 確認用
uint8_t read_relay_state; // リレー状態 確認用
uint8_t read_input_state; // 入力状態 確認用
bool relay[4]; // リレー1〜4 ON/OFFビット格納用
bool input[4]; // 入力端子1〜4 ON/OFFビット格納用
bool in_state[4]; // 入力状態保持用
// 関数 ********************************************************************
// スレーブアドレス設定関数(node0で実行)---------------------------------------
void setSlaveAddress(uint8_t address) {
// 0x10:保持レジスタ連続書込み
node0.setTransmitBuffer(0, address); // 書込む値を準備(バッファ番号, スレーブアドレスNo)
result = node0.writeMultipleRegisters(0x0000, 1); // 連続書込み実行(アドレス, 書込みレジスタ数)
delay(10); // 通信完了待ち
}
// スレーブアドレス読込み、接続設定関数 -----------------------------------------
void slaveConnectionSetting() {
// 0x03:保持レジスタ連続読込み
result = node0.readHoldingRegisters(0x0000, 1); // 保持レジスタ読込み(開始アドレス, 読込みレジスタ数)
read_slave_address = node0.getResponseBuffer(0); // 受信データバッファ取得
delay(10); // 通信完了待ち
// Modbus通信初期化
node1.begin(read_slave_address, Serial2); // 読み取ったスレーブアドレスでシリアル通信2を介して通信開始
}
// リレーON/OFF状態確認関数 --------------------------------------------------
void readRelayState() {
// 0x01:コイルON/OFF状態読込み(リレー1〜4状態読込み、下位4bitで取得)
result = node1.readCoils(0x0000, 8); // (開始アドレス, 読込みコイル数)
read_relay_state = node1.getResponseBuffer(0); // 受信データバッファ取得
delay(10); // 通信完了待ち
// リレーON/OFF確認ビットセット
relay[0] = read_relay_state & 0b0001; // リレー1ビットがONだったなら1セット
relay[1] = read_relay_state & 0b0010; // リレー2ビットがONだったなら1セット
relay[2] = read_relay_state & 0b0100; // リレー3ビットがONだったなら1セット
relay[3] = read_relay_state & 0b1000; // リレー4ビットがONだったなら1セット
}
// 入力端子ON/OFF状態確認関数 ------------------------------------------------
void readInputStatus() {
// 0x02:入力ON/OFF状態読込み(入力端子状態読込み)
result = node1.readDiscreteInputs(0x0000, 8); // (開始アドレス, 読込み入力数)
read_input_state = node1.getResponseBuffer(0); // 受信データバッファ取得
delay(10); // 通信完了待ち
// 入力端子ON/OFFビットセット
input[0] = read_input_state & 0b0001; // IN1が1なら1セット
input[1] = read_input_state & 0b0010; // IN1が2なら1セット
input[2] = read_input_state & 0b0100; // IN1が3なら1セット
input[3] = read_input_state & 0b1000; // IN1が4なら1セット
}
// リレーON/OFF出力関数 -----------------------------------------------------
void outputSingleRelay(uint8_t relay_num, uint16_t address, bool signal) {
// 0x05:コイルのON/OFF単独書込み
if (signal == false) { // リレー信号がfalseなら
node1.writeSingleCoil(address, 0x00); // リレー OFF実行
} else {
node1.writeSingleCoil(address, 0xFF); // リレー ON実行
}
delay(10); // 通信完了待ち
readRelayState(); // リレーON/OFF状態確認関数呼び出し
}
// 全リレーON/OFF出力関数 ----------------------------------------------------
void outputAllRelay(uint16_t value) {
// 0x0F:コイル状態連続書込み
node1.setTransmitBuffer(0, value); // 書込む値を準備(0x01FF/0x0100 = 全ON/全OFF)
result = node1.writeMultipleCoils(0x0000, 0x0008); // コイル状態連続書込み(アドレス, 書込みコイル数)
delay(10); // 通信完了待ち
readRelayState(); // リレーON/OFF状態確認関数呼び出し
}
// 初期設定 *******************************************************************
void setup() {
M5.begin(); // 本体初期化
// Modbus通信初期設定(SERIAL_8N1:データビット幅[8]、パリティチェック[No]、ストップビット[1])
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN); // シリアル通信2 初期化(RX, TX)
node0.begin(0, Serial2); // スレーブアドレス読み書き専用の通信をシリアル通信2で設定
delay(10); // 通信安定待ち
// スレーブアドレス設定
setSlaveAddress(SLAVE_NO); // スレーブアドレス設定関数呼び出し
slaveConnectionSetting(); // スレーブアドレス読込み、接続設定関数呼び出し
// 液晶画面初期設定
M5.Lcd.setTextColor(WHITE, BLACK); // 文字色
M5.Lcd.setTextFont(4); // フォント
}
// メイン処理 *******************************************************************
void loop() {
M5.update(); // ボタン状態初期化
// 入力端子の状態確認
readInputStatus(); // 入力端子ON/OFF状態確認関数呼び出し
// 入力端子によるリレーON/OFF単独出力、ON/OFF状態確認(入力状態保持により入力変化時のみ実行)
for(int i=0; i<4; i++) { // 入力端子の数だけ繰り返す
if(input[i] != in_state[i]) { // 入力状態が変化した場合のみ実行
outputSingleRelay(i, 0x0000+i, input[i]); // リレー出力実行(リレー番号, Coilアドレス, ON/OFF=True/false)
in_state[i] = input[i]; // 入力状態を更新する
}
}
// ボタン操作(設定値書込み)
if (M5.BtnA.wasPressed()) { // AボタンONならスレーブアドレス2に変更
setSlaveAddress(2); // スレーブアドレス設定関数呼び出し
slaveConnectionSetting(); // スレーブアドレス読込み、接続設定関数呼び出し
}
if (M5.BtnB.wasPressed()) { // BボタンONならスレーブアドレス3に変更
setSlaveAddress(3); // スレーブアドレス設定関数呼び出し
slaveConnectionSetting(); // スレーブアドレス読込み、接続設定関数呼び出し
}
if (M5.BtnC.wasPressed()) { // CボタンONで全リレー ON
outputAllRelay(0x01FF); // 全リレーON/OFF出力関数呼び出し(書込む値を指定:0x01FF/0x0100 = 全ON/全OFF)
}
if (M5.BtnC.wasReleased()) { // CボタンOFFで全リレー OFF
outputAllRelay(0x0100); // 全リレーON/OFF出力関数呼び出し(書込む値を指定:0x01FF/0x0100 = 全ON/全OFF)
}
// 液晶表示
M5.Lcd.setCursor(0, 0); // 表示座標
M5.Lcd.printf("Slave No.%02d\n", read_slave_address); // スレーブアドレス表示
// 入力端子1〜4 状態確認表示
M5.Lcd.printf("Input : %d, %d, %d, %d\n", input[0], input[1], input[2], input[3]);
// リレー1〜4 状態確認表示
M5.Lcd.printf("Relay : %d, %d, %d, %d\n", relay[0], relay[1], relay[2], relay[3]);
// エラー表示
if (result != 0) { // 通信成功以外ならエラー表示
M5.Lcd.fillScreen(BLACK); // 背景色
M5.Lcd.setCursor(0, 0); // 表示座標
M5.Lcd.println("ERROR"); // 「ERROR」表示
M5.Lcd.printf("0x%02X", result); // 16進数の「エラーコード」表示
}
delay(100); // 遅延時間
}
・初期設定詳細
Modbus通信のための初期設定について、サンプルプログラムから抜粋して紹介します。
#include <M5Stack.h> // CORE2の場合は <M5Core2.h>
#include "ModbusMaster.h" // modbusライブラリのインクルード
#define RX_PIN 16 // シリアル通信端子番号(RX)※CORE2は13
#define TX_PIN 17 // シリアル通信端子番号(TX)※CORE2は14
#define SLAVE_NO 1 // スレーブアドレス指定
ModbusMaster node0; // スレーブアドレス読み書き専用の通信用インスタンスを作成
ModbusMaster node1; // 指定したスレーブアドレスとの通信用インスタンスを作成
// 変数宣言
uint8_t result = 0; // 通信結果取得用
まずは上コードの「2行目」で「ModbusMaster」ライブラリをインクルードします。
次に「8,9行目」で「ModbusMaster」のインスタンスを作成し「node0」「node1」として使用できるようにします。
「12行目」では通信結果を格納するための変数「result」を宣言します。
通信成功の場合は「0」が格納されるため「0以外」の場合は通信エラーとなります。
エラー時には格納されたエラー番号を確認できるため、エラーの原因を探ることができます。
次に初期設定を行います。以下にサンプルプログラムから抜粋して紹介します。
void setup() {
M5.begin(); // 本体初期化
// Modbus通信初期設定(SERIAL_8N1:データビット幅[8]、パリティチェック[No]、ストップビット[1])
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN); // シリアル通信2 初期化(RX, TX)
node0.begin(0, Serial2); // スレーブアドレス読み書き専用の通信をシリアル通信2で設定
delay(10); // 通信安定待ち
// スレーブアドレス設定
setSlaveAddress(SLAVE_NO); // スレーブアドレス設定関数呼び出し
slaveConnectionSetting(); // スレーブアドレス読込み、接続設定関数呼び出し
// 液晶画面初期設定
M5.Lcd.setTextColor(WHITE, BLACK); // 文字色
M5.Lcd.setTextFont(4); // フォント
}
上コードの「4行目」で「Modbus」通信を行うためのシリアル通信の初期設定を行います。
「SERIAL_8N1」で通信仕様を設定します。
今回のリレーユニットでは「データビット幅[8bit]、パリティチェック[No(なし)]、ストップビット[1bit]」です。
シリアル通信の端子番号は「Serial2」の「M5Stack Basic」の標準端子「RX=16/TX=17」を指定しています。
「5行目」で以下のように指定して「node0」でModbus通信を開始します。
- スレーブアドレス:通信対象機器のスレーブアドレスを指定。ここでは「0」
- 使用するシリアル通信:Modbus通信に使用するシリアル通信名を指定。今回は「Serial2」
「9行目」では現在のスレーブアドレスを読込んで、現在のスレーブアドレスでModbus通信を開始できるように「node1」の通信設定を行う関数を呼び出します。
・スレーブアドレスの設定方法(保持レジスタ連続書込み:0x10)
スレーブアドレスを設定するための方法について、サンプルプログラムから抜粋して紹介します。
// スレーブアドレス設定関数(node0で実行)---------------------------------------
void setSlaveAddress(uint8_t address) {
// 0x10:保持レジスタ連続書込み
node0.setTransmitBuffer(0, address); // 書込む値を準備(バッファ番号, スレーブアドレスNo)
result = node0.writeMultipleRegisters(0x0000, 1); // 連続書込み実行(アドレス, 書込みレジスタ数)
delay(10); // 通信完了待ち
}
上コードの「4,5行目」で保持レジスタの連続書込みを実行して、スレーブアドレスが格納されている「保持レジスタ」の値を変更しています。
連続書込みコマンドの詳細は以下のようになります。
result = node0.writeMultipleRegisters(開始アドレス, 書込みレジスタ数);
- node0:スレーブアドレス「0」との通信を表します。(アドレスは初期設定で設定しておく)
- setTransmitBuffer:複数ある送信データは、このコマンドで事前に送信データバッファとして準備しておきます。(今回は1つだけです。)
- 送信データバッファ番号:書込むために送信するデータ番号を送信データ数分「0」から順番に「0, 1, 2〜」のように指定します。
- 書込みデータ:書込むデータを「送信データバッファ番号」ごとに指定します。
- writeMultipleRegisters:Modbus通信のファンクションコード「0x10」を表し「保持レジスタ」の内容を連続書込みするためのコマンドです。
「開始アドレス」と「書込みレジスタ数」を指定することで、連続して値を変更することができます。
今回使用したリレーユニットは「node0」の「保持レジスタ」のアドレス「0x0000」にスレーブアドレスが格納されているためこの1つだけを変更しています。 - 開始アドレス:連続書込み対象の「保持レジスタ」の最初のレジスタアドレスを指定します。
- 書込みレジスタ数:連続書き込みを行うレジスタ数を指定します。
- result:通信結果が格納されます。通信成功の場合は「0」で「0以外」は通信エラーです。
送受信データ構成について(保持レジスタ連続書込み時)
スレーブアドレスを書込むために以下を実行したときに、実際に送受信されるデータについて確認します。
// 0x10:保持レジスタ連続書込み
node0.setTransmitBuffer(0, 1); // 書込む値を準備(バッファ番号, スレーブアドレスNo)
result = node0.writeMultipleRegisters(0x0000, 1); // 連続書込み実行(アドレス, 書込みレジスタ数)
送信される(リクエスト)データは16進数で表すと以下のようになります。
※スレーブアドレス「1」を書き込んだ場合
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(保持レジスタ連続書込み)
3byte目:開始アドレス上位
4byte目:開始アドレス下位
5byte目:書込みレジスタ数上位
6byte目:書込みレジスタ数下位
7byte目:データバイト数
8byte目:送信データバッファ番号「0」の書込みデータ上位
9byte目:送信データバッファ番号「0」の書込みデータ下位
12,13byte目:送信データから生成されたエラーチェック用の「CRCコード」
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(保持レジスタ連続書込み)
3byte目:スレーブアドレス上位
4byte目:スレーブアドレス下位
5byte目:書込みレジスタ数上位
6byte目:書込みレジスタ数下位
7, 8byte目:受信データから生成されたエラーチェック用の「CRCコード」
・スレーブアドレスの確認方法(保持レジスタ読込:0x03)
スレーブアドレスを確認するための方法について、サンプルプログラムから抜粋して紹介します。
// スレーブアドレス読込み、接続設定関数 -----------------------------------------
void slaveConnectionSetting() {
// 0x03:保持レジスタ連続読込み
result = node0.readHoldingRegisters(0x0000, 1); // 保持レジスタ読込み(開始アドレス, 読込みレジスタ数)
read_slave_address = node0.getResponseBuffer(0); // 受信データバッファ取得
delay(10); // 通信完了待ち
// Modbus通信初期化
node1.begin(read_slave_address, Serial2); // 読み取ったスレーブアドレスでシリアル通信2を介して通信開始
}
上コードの「4行目」でスレーブアドレスが格納されている「保持レジスタ」の値を読み込んでいます。
保持レジスタ読込みコマンドの詳細は以下のようになります。
- node0:スレーブアドレス「0」との通信を表します。(アドレスは初期設定で設定しておく)
- readHoldingRegisters:Modbus通信のファンクションコード「0x03」を表し「保持レジスタ」の内容を読込みするためのコマンドです。
スレーブアドレスは「保持レジスタ」に格納されているため「開始アドレス」と「書込みレジスタ数」を指定することで、値を読み込むことができます。 - 開始アドレス:読込み対象の「保持レジスタ」の最初のレジスタアドレスを指定します。
- 読込みレジスタ数:読込みを行うレジスタ数を指定します。1つだけ読み込む場合は「1」です。
- result:通信結果が格納されます。通信成功の場合は「0」で「0以外」は通信エラーです。
「5行目」で以下のように受信したデータをバッファとして以下のように取得します。
- node0.getResponseBuffer(0):これでスレーブから送信されてきたデータを取得できます。
- read_slave_address:取得したデータを格納する変数です。
受信するデータのバイト数に合わせて準備しておく必要があります。
「9行目」で受信したスレーブアドレスで、Modbus通信できるように「node1」の初期設定を以下のように行っています。
送受信データ構成について(保持レジスタ読取り時)
「保持レジスタ」に格納されているスレーブアドレスを取得するために以下を実行したときに、実際に送受信されるデータについて確認します。
// 0x03:保持レジスタ連続読込み
result = node0.readHoldingRegisters(0x0000, 1); // 保持レジスタ読込み(開始アドレス, 読込みレジスタ数)
送信される(リクエスト)データは16進数で表すと以下のようになります。
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(保持レジスタ連続読出し)
3byte目:開始アドレス上位
4byte目:開始アドレス下位
5byte目:読出しレジスタ数上位
6byte目:読出しレジスタ数下位
7,8byte目:送信データから生成されたエラーチェック用の「CRCコード」
スレーブ側から送信されてくる受信(レスポンス)データは以下のようになります。
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(保持レジスタ連続読出し)
3byte目:データバイト数
4byte目:スレーブアドレス上位
5byte目:スレーブアドレス下位
6, 7byte目:受信データから生成されたエラーチェック用の「CRCコード」
・リレーのON/OFF状態確認方法(コイルON/OFF状態読込み:0x01)
リレーのON/OFF状態を確認するための方法について、サンプルプログラムから抜粋して紹介します。
// リレーON/OFF状態確認関数 --------------------------------------------------
void readRelayState() {
// 0x01:コイルON/OFF状態読込み(リレー1〜4状態読込み、下位4bitで取得)
result = node1.readCoils(0x0000, 8); // (開始アドレス, 読込みコイル数)
read_relay_state = node1.getResponseBuffer(0); // 受信データバッファ取得
delay(10); // 通信完了待ち
// リレーON/OFF確認ビットセット
relay[0] = read_relay_state & 0b0001; // リレー1ビットがONだったなら1セット
relay[1] = read_relay_state & 0b0010; // リレー2ビットがONだったなら1セット
relay[2] = read_relay_state & 0b0100; // リレー3ビットがONだったなら1セット
relay[3] = read_relay_state & 0b1000; // リレー4ビットがONだったなら1セット
}
上コードの「4行目」でリレーのON/OFF状態が格納されている「コイル」の値を読み込んでいます。
「コイル」の値を読み込むための詳細は以下のようになります。
- node1:スレーブアドレス「1」との通信を表します。(アドレスは関数内で事前に設定しています。)
- readCoils:Modbus通信のファンクションコード「0x01」を表し「コイル」の内容を読込みするためのコマンドです。
「開始アドレス」と「読込みコイル数」を指定することで、値を読み込むことができます。 - 開始アドレス:読込み対象の「コイル」の最初のアドレスを指定します。
- 読込みコイル数:読込みを行うコイル数を指定します。このリレーユニットでは「8」を指定した時のみ、4つのリレーの状態を「下位4bit」で取得することができます。
- result:通信結果が格納されます。通信成功の場合は「0」で「0以外」は通信エラーです。
「5行目」で以下のように受信したデータをバッファとして以下のように取得します。
- node1.getResponseBuffer(0):これでスレーブから送信されてきたデータを取得できます。
- read_relay_state:取得したデータを格納する変数です。
送受信データ構成について(コイルON/OFF状態読込み時)
リレーのON/OFF状態を取得するために以下を実行したときに、実際に送受信されるデータについて確認します。
// 0x01:コイルON/OFF状態読込み(リレー1〜4状態読込み、下位4bitで取得)
result = node1.readCoils(0x0000, 8); // (開始アドレス, 読込みコイル数)
送信される(リクエスト)データは16進数で表すと以下のようになります。
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(コイルON/OFF状態読込)
3byte目:開始アドレス上位
4byte目:開始アドレス下位
5byte目:読出しコイル数上位
6byte目:読出しコイル数下位
7,8byte目:送信データから生成されたエラーチェック用の「CRCコード」
スレーブ側から送信されてくる受信(レスポンス)データは以下のようになります。
※全てのリレーがOFFの場合
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(コイルON/OFF状態読込)
3byte目:データバイト数
4byte目:コイルON/OFF状態(下位4bitで確認)
5, 6byte目:受信データから生成されたエラーチェック用の「CRCコード」
・入力端子のON/OFF状態確認方法(入力ステータス読込み:0x02)
入力端子のON/OFF状態を確認するための方法について、サンプルプログラムから抜粋して紹介します。
// 入力端子ON/OFF状態確認関数 ------------------------------------------------
void readInputStatus() {
// 0x02:入力ON/OFF状態読込み(入力端子状態読込み)
result = node1.readDiscreteInputs(0x0000, 8); // (開始アドレス, 読込み入力数)
read_input_state = node1.getResponseBuffer(0); // 受信データバッファ取得
delay(10); // 通信完了待ち
// 入力端子ON/OFFビットセット
input[0] = read_input_state & 0b0001; // IN1が1なら1セット
input[1] = read_input_state & 0b0010; // IN1が2なら1セット
input[2] = read_input_state & 0b0100; // IN1が3なら1セット
input[3] = read_input_state & 0b1000; // IN1が4なら1セット
}
入力端子のON/OFF状態(入力ステータス)を確認する方法は、上で紹介した、リレーのON/OFF(コイル)の状態を読込むための方法とほぼ同じで、上コード「4行目」のように行います。
送受信データ構成について(入力ステータス読込み時)
送受信されるデータも「コイル」の時とほぼ同じで「ファンクションコード(以下の2byte目)」の部分が「01」から「02」に変わるだけで、16進数で表すと送受信でそれぞれ以下のようになります。
受信データ:01 02 01 0F E1 8C
※全ての入力端子がONの場合
・リレーのON/OFF方法(コイルON/OFF単独書込み:0x05)
リレーのON/OFF出力を制御する方法について、サンプルプログラムから抜粋して紹介します。
// リレーON/OFF出力関数 -----------------------------------------------------
void outputSingleRelay(uint8_t relay_num, uint16_t address, bool signal) {
// 0x05:コイルのON/OFF単独書込み
if (signal == false) { // リレー信号がfalseなら
node1.writeSingleCoil(address, 0x00); // リレー OFF実行
} else {
node1.writeSingleCoil(address, 0xFF); // リレー ON実行
}
delay(10); // 通信完了待ち
readRelayState(); // リレーON/OFF状態確認関数呼び出し
}
上コードの「5行目」と「7行目」で「コイル」のON/OFF状態を変更して、リレーのON/OFF出力を行います。
コイルのON/OFF状態書込みコマンドの詳細は以下のようになります。
- node1:スレーブアドレス「1」との通信を表します。(アドレスは関数内で事前に設定しています。)
- writeSingleCoil:Modbus通信のファンクションコード「0x05」を表し「コイル」のON/OFF状態を変更するためのコマンドです。
「コイル」は「リレー1〜4」の4つあり、個別に変更したい場合はこのコマンドを使用します。 - アドレス:書込み対象の「コイル」のアドレス「0x0000〜0x0003」を指定します。
- 書込みデータ:書込みデータとして「0xFF00 = ON/ 0x0000 = OFF」を指定します。
- result:通信結果が格納されます。通信成功の場合は「0」で「0以外」は通信エラーです。
送受信データ構成について(コイルON/OFF単独書込み時)
リレー1をONするために以下を実行したときに、実際に送受信されるデータについて確認します。
// 0x05:コイルのON/OFF単独書込み
node1.writeSingleCoil(0x0000, 0xFF); // リレー ON実行
送信される(リクエスト)データは16進数で表すと以下のようになります。
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(コイルON/OFF状態書込み)
3byte目:レジスタアドレス上位
4byte目:レジスタアドレス下位
5byte目:書込みデータ上位(ON = FF/ OFF = 00)
6byte目:書込みデータ下位
7,8byte目:送信データから生成されたエラーチェック用の「CRCコード」
・全リレーのON/OFF方法(コイルON/OFF連続書込み:0x0F)
全てのリレーのON/OFF出力を一括で制御する方法について、サンプルプログラムから抜粋して紹介します。
// 全リレーON/OFF出力関数 ----------------------------------------------------
void outputAllRelay(uint16_t value) {
// 0x0F:コイル状態連続書込み
node1.setTransmitBuffer(0, value); // 書込む値を準備(0x01FF/0x0100 = 全ON/全OFF)
result = node1.writeMultipleCoils(0x0000, 0x0008); // コイル状態連続書込み(アドレス, 書込みコイル数)
delay(10); // 通信完了待ち
readRelayState(); // リレーON/OFF状態確認関数呼び出し
}
上コードの「4,5行目」でコイルの連続書込みを実行して、「コイル」のON/OFF状態を連続で変更して、4つのリレーを一括でON/OFF出力を行います。
連続書込みコマンドの詳細は以下のようになります。
result = node1.writeMultipleRegisters(開始アドレス, 書込みコイル数);
- node1:スレーブアドレス「1」との通信を表します。(アドレスは関数内で事前に設定しています。)
- setTransmitBuffer:複数ある送信データは、このコマンドで事前に送信データバッファとして準備しておきます。(今回の場合は1つだけ)
- 送信データバッファ番号:書込むために送信するデータ番号を「0」から順番に指定します。
- 書込みデータ:書込むデータを「送信データバッファ番号」ごとに指定します。
(0x01FF/0x0100 = 全ON/全OFF) - writeMultipleRegisters:Modbus通信のファンクションコード「0x0F」を表し「コイル」の内容を連続書込みするためのコマンドです。
4つのリレーに対して4つの「コイル」があり「開始アドレス」と「書込みコイル数」を指定することで、連続して値を変更することができます。 - 開始アドレス:連続書込み対象の「コイル」の最初のアドレスを指定します。
- 書込みレジスタ数:連続書き込みを行うコイル数を指定します。
- result:通信結果が格納されます。通信成功の場合は「0」で「0以外」は通信エラーです。
送受信データ構成について(コイルON/OFF連続書込み時)
4つのリレーの全てONするために「コイル」の値を連続で書込むために以下を実行したときに、実際に送受信されるデータについて確認します。
// 0x0F:コイル状態連続書込み
node1.setTransmitBuffer(0, 0x01FF); // 書込む値を準備(0x01FF/0x0100 = 全ON/全OFF)
result = node1.writeMultipleCoils(0x0000, 0x0008); // コイル状態連続書込み(アドレス, 書込みコイル数)
送信される(リクエスト)データは16進数で表すと以下のようになります。
※全てのリレーをONする場合
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(コイルON/OFF連続書込み)
3byte目:開始アドレス上位
4byte目:開始アドレス下位
5byte目:書込みレジスタ数上位
6byte目:書込みレジスタ数下位
7byte目:データバイト数
8byte目:書込みデータ(全ON = FF/ 全OFF = 00)
9,10byte目:送信データから生成されたエラーチェック用の「CRCコード」
データ構成は左から
1byte目:スレーブアドレス
2byte目:ファンクションコード(コイルON/OFF連続書込み)
3byte目:スレーブアドレス上位
4byte目:スレーブアドレス下位
5byte目:書込みレジスタ数上位
6byte目:書込みレジスタ数下位
7, 8byte目:受信データから生成されたエラーチェック用の「CRCコード」
5.まとめ
M5StackでModbus通信を使用した4チャンネルのリレーユニットの使い方を詳しく紹介しました。
Modbus通信を使用したリレーユニットにはいろいろなものがあり、今回は4チャンネルでしたが1チャンネル〜8チャンネルと種類も豊富です。
通信プロトコルについては、それぞれで固有のものもあると思いますが、基本的なリレーのON/OFF方法は「コイル(0x05)」を操作し、入力端子を持ったものは「入力ステータス(0x02)」で入力状態の確認ができると思います。
M5Stack等のマイコンボードでは入出力端子の数が限られており、各端子1つづつでリレー出力や入力確認を行おうとすると足りなくなったり、配線も複雑になります。
今回のリレーユニットを使用すれば、Modbus通信用の2本の配線だけで4つのリレーのON/OFF制御と4つの入出力端子の状態確認ができるため、複数の入出力を持ったPLC(シーケンサ)としても使用できます。
複数の入出力を制御したいときには、安価に実現できると思うので使用を検討してみてください。
コメント