「I2C通信」で複数のリレーをON/OFFできるI2Cリレーの使用方法について詳しく紹介します。
ライブラリを使用して簡単に制御できますが、単純な「I2C通信」で駆動できるため、ライブラリを使用せずにON/OFFする方法についてもサンプルプログラムで紹介しています。
「M5NanoC6」については、以下のリンクで詳しく紹介しています。
「I2C通信」の使い方については、以下のリンクで詳しく紹介しています。
1.使用するI2Cリレーについて
2.I2C通信とは
3.I2Cリレーの通信仕様確認
4.サンプルプログラム(ライブラリ使用)
・ライブラリの使い方
5.サンプルプログラム(ライブラリ未使用)
・プログラムの詳細
6.I2C通信を行うArduinoコマンドについて
・初期設定
・データの受信(読み取り)方法
・データの送信(書き込み)方法
7.まとめ
1.使用するI2Cリレーについて
今回使用する「I2Cリレー」は「M5Stack社」の「4チャンネルリレーユニット」で、外観は以下のようになります。
主な仕様は以下表のようになります。
項目 | 仕様 |
---|---|
開閉電圧 | AC-250V/DC-28V |
定格電流(抵抗負荷) | 10A |
最大電流 | 16A |
通信方式 | I2C通信 デバイスアドレス:0x26 |
重量 | 40g |
サイズ | 112×23×18mm |
2.I2C通信とは
I2C通信とは「アイスクエアドシー」と読み、通称「アイ・ツー・シー」と呼ばれます。
フィリップス社によって開発されたシリアル通信方法で、同じ基板内などの近距離デバイス同士の通信を目的に開発されたものです。
通信速度はスタンダードモードで最大100kbps、ファストモードで最大400kbps、さらに高速な通信モードもありますがArduinoコマンドを使用する場合は標準でスタンダードモードとなります。
通信はマスターとスレーブ間で行われ、スレーブとしてデバイスアドレスが違うものであれば1つのマスターからの指示で複数のデバイスを制御することができます。
3.I2Cリレーの通信仕様確認
今回使用するI2Cリレーのデバイスアドレスは「0x26」です。
リレーの制御を行うために使用するレジスタは以下の2つだけです。
①動作モード選択レジスタ:0x10
リレーとLEDを同期(同時にON/OFF)させるか、非同期(個別にON/OFF)させるかを設定できます。設定は「0x10」レジスタの0ビット目で以下のように行います。
1:同期
②リレー(LED)制御レジスタ:0x11
「0x11」レジスタの8ビットが以下表のように「4つのリレー」と「4つのLED」に対応しており、各ビットを「1」でON、「0」でOFFすることができます。
Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
LED4 | LED3 | LED2 | LED1 | リレー4 | リレー3 | リレー2 | リレー1 |
4.サンプルプログラム(ライブラリ使用)
まずはライブラリを使用して簡単に各リレーをON/OFFする方法を紹介します。
配線は下画像のように「4チャンネルリレーユニット」を付属の「GROVEコネクタ配線」で接続するだけです。
使用するライブラリは以下になります。
事前に開発環境にインストールしておいてください。
UNIT_4RELAY
サンプルプログラムは以下になります。「コピペ」して書き込んでください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
#include "Arduino.h"
#include "Unit_4RELAY.h" // 4-Relay用制御用ライブラリ
#include <Wire.h> // I2C通信制御用
UNIT_4RELAY relay;
#define BLUE_LED_PIN 7 // 青色LED端子番号
#define BTN_PIN 9 // 本体ボタン端子番号
bool old_state = true; // 前回のボタン状態を格納
// 初期設定 ---------------------------------------------------
void setup() {
Serial.begin(9600);
relay.begin(&Wire, 2, 1); // I2C通信用端子設定(SDA, SCL)
relay.Init(1); // リレー&LED同期モード設定(0:非同期, 1:同期)
relay.relayAll(0); // 全リレーOFF(0:OFF, 1:ON)
// 入力設定
pinMode(BTN_PIN, INPUT); // 本体ボタン
// 出力設定
pinMode(BLUE_LED_PIN, OUTPUT); // 本体LED青
}
// メイン -----------------------------------------------------
void loop() {
bool btn_state = digitalRead(BTN_PIN); // 現在のボタン状態を取得
if (old_state != btn_state) { // ボタンの状態に変化があれば
old_state = btn_state; // 前回のボタン状態を更新
if (btn_state == LOW) { // ボタンが押された(LOW)なら
digitalWrite(BLUE_LED_PIN, HIGH); // 本体LED消灯
relay.relayWrite(0, 1); // リレーON (リレー番号-1, [0:OFF, 1:ON])
// relay.ledWrite(0, 1); // LED ON (LED 番号-1, [0:OFF, 1:ON])(非同期時)
Serial.println("ON");
} else { // ボタンが離された(HIGH)なら
digitalWrite(BLUE_LED_PIN, LOW); // 本体LED点灯
relay.relayWrite(0, 0); // リレーOFF (リレー番号-1, [0:OFF, 1:ON])
// relay.ledWrite(0, 0); // LED OFF (LED 番号-1, [0:OFF, 1:ON])(非同期時)
Serial.println("OFF");
}
}
delay(50);
}
・ライブラリの使い方
ライブラリを使用して各リレー(LED)を制御するには、まず以下のようにライブラリを読み込みます。「M5NanoC6」の場合は「I2C通信」を使用するために「Wire.h」もインクルードしておく必要があります。
#include "Unit_4RELAY.h" // 4-Relay用制御用ライブラリ
#include <Wire.h> // I2C通信制御用
次に「UNIT_4RELAY」のインスタンスを「relay」として準備しておきます。
決め事のようなものなので、このまま書いておきます。
UNIT_4RELAY relay;
初期設定(setup)では以下コード「3行目」のように「I2C通信」に使用する端子を設定します。
今回は「GROVEコネクタ」の信号端子「2, 1」を設定しています。
次に以下コード「4行目」のようにリレーのON/OFFに同期して、リレーに対応するLEDもON/OFFさせるかのモード(同期、非同期)を設定します。ここは個別にLEDを制御する理由がなければ同期モードで以下のようにしておきましょう。
初期設定の最後には特に理由がなければ、全てのリレー(LED)をOFFで初期化しておきましょう。
// 初期設定 ---------------------------------------------------
void setup() {
relay.begin(&Wire, 2, 1); // I2C通信用端子設定(SDA, SCL)
relay.Init(1); // リレー&LED同期モード設定(0:非同期, 1:同期)
relay.relayAll(0); // 全リレーOFF(0:OFF, 1:ON)
// relay.ledAll(0); // 全LED OFF(0:OFF, 1:ON) ※非同期設定時
}
リレー、LEDのON/OFFは以下のように指定します。
// 全リレー制御
relay.relayAll(1); // 全リレーON (0:OFF, 1:ON)
relay.relayAll(0); // 全リレーOFF
// 全リレー制御(非同期設定時)
relay.ledAll(1); // 全LED ON (0:OFF, 1:ON)
relay.ledAll(0); // 全LED OFF
// リレー ON
relay.relayWrite(0, 1); // リレー1 ON (リレー番号-1, [0:OFF, 1:ON])
relay.relayWrite(1, 1); // リレー2 ON
relay.relayWrite(2, 1); // リレー3 ON
relay.relayWrite(3, 1); // リレー4 ON
// リレー OFF
relay.relayWrite(0, 0); // リレー1 OFF (リレー番号-1, [0:OFF, 1:ON])
relay.relayWrite(1, 0); // リレー2 OFF
relay.relayWrite(2, 0); // リレー3 OFF
relay.relayWrite(3, 0); // リレー4 OFF
// LED ON(非同期設定時)
relay.ledWrite(0, 1); // LED1 ON (LED 番号-1, [0:OFF, 1:ON])
relay.ledWrite(1, 1); // LED2 ON
relay.ledWrite(2, 1); // LED3 ON
relay.ledWrite(3, 1); // LED4 ON
// LED OFF(非同期設定時)
relay.ledWrite(0, 0); // LED1 OFF (LED 番号-1, [0:OFF, 1:ON])
relay.ledWrite(1, 0); // LED2 OFF
relay.ledWrite(2, 0); // LED3 OFF
relay.ledWrite(3, 0); // LED4 OFF
5.サンプルプログラム(ライブラリ未使用)
ライブラリを使用すれば簡単に各リレー(LED)をON/OFFできますが、ライブラリ無しでも比較的簡単に使用することができます。
サンプルプログラムは以下になります。「コピペ」して書き込んでください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
#include "Arduino.h"
#include <Wire.h> // I2C通信制御用
#define RELAY_ADDR 0x26 // 4リレーユニットI2C通信アドレス
#define MODE_REG 0x10 // モードコントロールレジスタ(Bit:0 / Sync Mode 0:LED Async 1:LED Sync)
#define RELAY_REG 0x11 // リレー(LED)コントロールレジスタ
#define BLUE_LED_PIN 7 // 青色LED端子番号
#define BTN_PIN 9 // 本体ボタン端子番号
// 変数宣言
bool old_state = true; // 前回のボタン状態を格納
uint8_t relay; // リレー制御ビット指定用
uint8_t old_relay; // 前回のリレー制御ビット状態を格納
/********************** リレー出力実行関数 **********************/
void relayControll(int8_t addr, int8_t data) {
// I2C通信データ送信(書き込み)
Wire.beginTransmission(RELAY_ADDR); // デバイスアドレスを指定して通信開始
Wire.write(addr); // 書き込みレジスタアドレス指定
Wire.write(data); // データ書き込み
Wire.endTransmission(true); // 書き込み完了
}
// 初期設定 ---------------------------------------------------
void setup() {
Serial.begin(9600); // シリアル通信初期化(ArduinoIDEの「USB CDC On Boot:」設定は"Enable"にしておく)
Wire.begin(2, 1); // I2C通信初期化(SDA, SCL)、グローブコネクタの端子番号を指定
// 入力設定
pinMode(BTN_PIN, INPUT); // 本体ボタン
// 出力設定
pinMode(BLUE_LED_PIN, OUTPUT); // 本体LED青
// リレー動作モード設定
relayControll(MODE_REG, 0x00); // LED同期モードに設定 (非同期:0x00 / 同期:0x01)
// リレー初期化(全OFF)※全ONは0xFF
relay = 0x00;
relayControll(RELAY_REG, relay); // リレー全OFF設定
}
// メイン -----------------------------------------------------
void loop() {
// ボタン処理(ボタン状態に変化があった時にのみ実行)
bool btn_state = digitalRead(BTN_PIN); // 現在のボタン状態を取得
if (old_state != btn_state) { // ボタンの状態に変化があれば
old_state = btn_state; // 前回のボタン状態を更新
// ボタンON/OFF確認
if (btn_state == LOW) { // ボタンが押された(LOW)なら
digitalWrite(BLUE_LED_PIN, HIGH); // 本体LED点灯
relay |= 0b00000001; // リレー1 ON
relay |= 0b00010000; // LED1 ON(非同期時)
} else { // ボタンが離された(HIGH)なら
digitalWrite(BLUE_LED_PIN, LOW); // 本体LED消灯
relay &= 0b11111110; // リレー1 OFF
relay &= 0b11101111; // LED1 OFF(非同期時)
}
}
// リレー制御ビットに変化があればリレー出力を実行
if (old_relay != relay) { // ボタンの状態に変化があれば
old_relay = relay; // 前回のリレー状態を更新
relayControll(RELAY_REG, relay); // リレー出力実行関数
Serial.printf("Relay Change!: %02X\n", relay); // シリアル出力
}
delay(50); // 遅延時間
}
・プログラムの詳細
プログラムの詳細を確認するために、もう一度「4チャンネルリレーユニット」の通信仕様を確認しておきましょう。
以下のように「I2Cアドレス」は「0x26」です。
リレー動作のモードを設定する「レジスタ」は「0x10」です。
リレー(LED)の動作を設定する「レジスタ」は「0x11」です。
/*
4-Relay UNIT
I2C Address : 0x26
Mode control Reg : 0x10(Bit0でLED同期設定 0:LED 非同期 / 1:LED 同期)
RELAY control Reg : 0x11
| Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
| LED4 | LED3 | LED2 | LED1 |Relay4|Relay3|Relay2|Relay1| ※1:ON/0:OFF
*/
リレー(LED)の動作を設定する「レジスタ(0x11)」の8ビットは4つのリレーと4つのLEDに対応しています。
「0ビット目(Bit0)」を「0」にすると「リレー1」が「OFF」します。
レジスタの値を指定するにはサンプルプログラム「16〜23行目」の「relayControll()」関数内で「I2C通信」で指定しています。
「I2C通信」を行う方法はこの後の「6.I2C通信を行うArduinoコマンドについて」で詳しく紹介しています。
/********************** リレー出力実行関数 **********************/
void relayControll(int8_t addr, int8_t data) {
// I2C通信データ送信(書き込み)
Wire.beginTransmission(RELAY_ADDR); // デバイスアドレスを指定して通信開始
Wire.write(addr); // 書き込みレジスタアドレス指定
Wire.write(data); // データ書き込み
Wire.endTransmission(true); // 書き込み完了
}
この関数を実行するには以下のように指定します。
・レジスタアドレス:設定を行う「レジスタ」のアドレスを指定します。
・値:レジスタの8ビットの値を設定します。
実際にサンプルプログラム「36行目」では以下のように「リレー動作モード」を設定しています。
#define MODE_REG 0x10 // モードコントロールレジスタ(Bit:0 / Sync Mode 0:LED Async 1:LED Sync)
// リレー動作モード設定
relayControll(MODE_REG, 0x00); // LED同期モードに設定 (非同期:0x00 / 同期:0x01)
「39行目」では以下のように指定して、リレーの初期化(全OFF)を設定しています。
#define RELAY_REG 0x11 // リレー(LED)コントロールレジスタ
// リレー初期化(全OFF)※全ONは0xFF
relay = 0x00;
relayControll(RELAY_REG, relay); // リレー全OFF設定
「63行目」では以下のように指定して、各リレー(LED)のON/OFFを指定しています。
この時、各リレー(LED)の状態は変数「relay」で8ビットで指定します。
relayControll(RELAY_REG, relay); // リレー出力実行関数
変数「relay」の設定方法は以下のように「ビット演算」を使用します。
・対象のビットだけを「1」にする場合は「ビット演算」の「OR (|)」を使用。
・対象のビットだけを「0」にする場合は「ビット演算」の「AND (&)」を使用。
/*
// ビット操作でON/OFF指定、特定のビットをORでON(1)、ANDでOFF(0)
uint8_t relay = 0x00; // リレー制御ビット指定用(0x00:全OFF / 0xFF:全ON)
relay |= 0b00010001; // リレー1(LED4) ON : 0x11 ※relay |= (0x11 << 0)と同じ
relay |= 0b00100010; // リレー2(LED3) ON : 0x22 ※relay |= (0x11 << 1)と同じ
relay |= 0b01000100; // リレー3(LED2) ON : 0x44 ※relay |= (0x11 << 2)と同じ
relay |= 0b10001000; // リレー4(LED1) ON : 0x88 ※relay |= (0x11 << 3)と同じ
relay &= 0b11101110; // リレー1(LED4) OFF : 0xEE ※relay &= ~(0x11 << 0)と同じ
relay &= 0b11011101; // リレー2(LED3) OFF : 0xDD ※relay &= ~(0x11 << 1)と同じ
relay &= 0b10111011; // リレー3(LED2) OFF : 0xBB ※relay &= ~(0x11 << 2)と同じ
relay &= 0b01110111; // リレー4(LED1) OFF : 0x77 ※relay &= ~(0x11 << 3)と同じ
*/
実際にサンプルプログラム内では「52, 53行目」のように指定してリレー1(LED1)がONするように、変数「relay」の値を設定(対象のビットのみ1へ)しています。
relay |= 0b00000001; // リレー1 ON
relay |= 0b00010000; // LED1 ON(非同期時)
「56, 57行目」では以下のように指定してリレー1(LED1)がOFFするように、変数「relay」の値を設定(対象のビットのみ0へ)しています。
relay &= 0b11111110; // リレー1 OFF
relay &= 0b11101111; // LED1 OFF(非同期時)
最後に以下の「60〜65行目」のように変数「relay」の状態を監視して、変化があった時だけ「relayControll()」関数を実行するようにしています。
// リレー制御ビットに変化があればリレー出力を実行
if (old_relay != relay) { // ボタンの状態に変化があれば
old_relay = relay; // 前回のリレー状態を更新
relayControll(RELAY_REG, relay); // リレー出力実行関数
Serial.printf("Relay Change!: %02X\n", relay); // シリアル出力
}
6.I2C通信を行うArduinoコマンドについて
ライブラリを使用しないサンプルプログラムでは「I2C通信」で各リレー(LED)のON/OFFを指定しています。
使用するデバイスによって各機能を使用するための通信方法は異なるため、データシートで確認する必要がありますが「I2C通信」を行うための初期設定とデータの送受信方法は以下の数行だけなのでここで紹介しておきます。
・初期設定
「I2C通信」の初期設定は通信端子番号を指定して以下のように行います。
// 初期設定
void setup() {
Wire.begin(2, 1); // I2C通信端子指定(SDA, SCL)
}
・データの受信(読み取り)方法
「I2C通信」でのデータ受信は以下のような手順で行います。
// I2C通信データ受信(読み取り)
Wire.beginTransmission(I2C_ADDR); // デバイスアドレスを指定して通信開始
Wire.write(REG_ADDR1); // 読み取りレジスタアドレス指定して書き込み
Wire.write(REG_ADDR2); // 読み取りレジスタが複数ある時は続けてアドレス指定して書き込み
Wire.endTransmission(true); // 書き込み完了
delay(100) // データ受信待機(時間はデータシートで確認)
// データ受信(読み取り)
Wire.requestFrom(I2C_ADDR, byte); // 受信データリクエスト(I2Cアドレスと受信バイト数指定)
data = Wire.read(); // データを受信し変数(複数バイトは配列)に格納
①「Wire.beginTransmission」でデータを受信したい対象デバイスのアドレス(I2C_ADDR)を指定して通信を開始します。
②「Wire.write」で対象データのレジスタアドレス(REG_ADDR)を指定します。(必要に応じて同様に複数回指定)
③レジスタの指定が完了したら「Wire.endTransmission(true)」で一度通信を終了します。
④確実にデータ受信を行うために待機時間を設けます。(時間はデータシートで確認)
⑤スレーブ側から指定したレジスタのデータが送信されてくるので、データを受信するために「Wire.requestFrom」で対象デバイスのアドレス(I2C_ADDR)と受信バイト数(byte)を数値で指定してデータ受信を開始します。
⑥送信されてきたデータを「Wire.read()」で受信(読み取り)し変数に格納して使用します。
データが複数の場合は「for文」で連続して配列に格納する等して使用します。
・データの送信(書き込み)方法
I2C通信でのデータ送信(書き込み)は受信(読み込み)の時のレジスタアドレスの指定と同様の方法で以下のような手順で行います。
// I2C通信データ送信(書き込み)※受信の時と同じ手順
Wire.beginTransmission(I2C_ADDR); // デバイスアドレスを指定して通信開始
Wire.write(REG_ADDR); // 書き込みレジスタアドレス指定して書き込み
Wire.write(data1); // データ書き込み(複数の場合はfor文等で連続指定)
Wire.endTransmission(true); // 書き込み完了
①受信の時と同様に「Wire.beginTransmission」でデータを送信したい対象デバイスのアドレス(I2C_ADDR)を指定して通信を開始します。
②先に「Wire.write」で書き込み先のレジスタアドレス(REG_ADDR)を指定して書き込みます。
③続けて「Wire.write」で送信(書き込み)したいデータ(data1)を指定して書き込みます。
データが複数の場合は「for文」等で「Wire.write」を連続で実行して配列のデータを送信します。
④全てのデータを送信したら「Wire.endTransmission(true)」で送信(書き込み)完了です。
7.まとめ
「I2C通信」でON/OFFできる「I2Cリレー」の使用方法について詳しく紹介しました。
出力端子の少ない小型なデバイスで、出力点数が足りなくなった場合でも、2本の信号線で通信ができる「I2C通信」対応リレーを使用すれば比較的簡単に出力を増設でき、さらに「I2C通信」対応センサやモータドライバを接続して同時に使用することも可能です。
ライブラリを使用して簡単に制御できますが、単純な「I2C通信」で駆動できるため、ライブラリを使用せずに制御する方法も理解しておくと、プログラム容量が足りなくなった時にもすぐに対応できます。
また、リレーのON/OFFは「ビット演算」で設定するため「組み込みプログラム」では欠かせない「ビット演算」の学習にもなります。
今回は「M5NanoC6」の出力増設のために「Modbus通信」のリレーと迷ったのですが、センサーやドライバのI2C対応デバイスが豊富なので「I2C通信」のリレーを先に紹介することにしました。
残念ながら「I2C通信」に対応したリレーは少ないです・・・なんででしょう?「I/Oエキスパンダー」で使うからでしょうか?この場合は配線が増えるので、対応したリレーがあるとありがたいんですが^^;
いずれにしても「M5NanoC6」のような小型なデバイスは今後増えてくると思うので、通信対応機器の利用も増えそうです。
今後は「Modbus通信」や「I/Oエキスパンダー」を使用した出力増設についても紹介していきたいと思います。
コメント