【UniRx & Coroutine & async/await】Unityでシリアルポート読み取り
Unityでシリアル通信(受信)する方法です。UniRx版、コルーチン版、async/await版を記載します。
1. 準備
スクリプトからシリアルポートを操作できるようにするために、Unityの Project Settings
> Player
> Other Settings
の項目の Api Compatibility Level
を .NET 4.x
に設定します。
また、受信データ表示用にヒエラルキーにTextを追加してください。 Debug.Logで確認する場合は不要です。
2. スクリプト (UniRx版)
以下のスクリプトを適当なゲームオブジェクトにアタッチします。 ポート名は環境に合わせて変更してください。
using System.IO.Ports; using UnityEngine; using UniRx; using UnityEngine.UI; using System; public class MainScript : MonoBehaviour { private string portName = "COM4"; private int baurate = 9600; private SerialPort serial; private bool isLoop = true; [SerializeField] Text countText = default; void Start() { serial = new SerialPort (portName, baurate, Parity.None, 8, StopBits.One); serial.Open(); Observable.Create<string>(ReadData) .SubscribeOn(Scheduler.ThreadPool) .ObserveOn(Scheduler.MainThread) .Subscribe(data => countText.text = data) .AddTo(this); } private IDisposable ReadData(IObserver<string> observer) { while (isLoop) { observer.OnNext(serial.ReadLine()); } return Disposable.Empty; } private void OnDestroy() { isLoop = false; serial.Close(); } }
内容としてはシリアルポートの読み取りをThreadPoolで、結果をTextに表示するために読み取ったデータをMainThreadで処理しています。 (GUIを操作する場合はMainThreadで行う必要があります。Debug.Logするだけであればスレッドの切り替えは不要です)
3. スクリプト (コルーチン版)
以下のスクリプトを適当なゲームオブジェクトにアタッチします。 ポート名は環境に合わせて変更してください。
using System.Collections; using UnityEngine; using System.IO.Ports; using UnityEngine.UI; public class MainScript : MonoBehaviour { private string portName = "COM4"; private int baurate = 9600; private SerialPort serial; private bool isLoop = true; [SerializeField] Text countText = default; void Start() { serial = new SerialPort (portName, baurate, Parity.None, 8, StopBits.One); serial.Open(); StartCoroutine(ReadData()); } private IEnumerator ReadData() { while (isLoop) { countText.text = this.serial.ReadLine(); yield return null; } } private void OnDestroy() { isLoop = false; serial.Close(); } }
4. スクリプト (async/await版)
(2019.12.07 async/await版も追加しました)
using UnityEngine; using System.IO.Ports; using System.Threading.Tasks; public class MainScript : MonoBehaviour { private string portName = "COM4"; private int baurate = 9600; private SerialPort serial; private bool isLoop = true; [SerializeField] Text countText = default; async void Start() { serial = new SerialPort (portName, baurate, Parity.None, 8, StopBits.One); serial.Open(); await ReadData(); } private async Task ReadData() { await Task.Run(() => { while (isLoop) { countText.text = this.serial.ReadLine(); } }); } private void OnDestroy() { isLoop = false; serial.Close(); } }
5. 動作確認準備
今回、動作確認用にArduino Unoを使用します。Arduinoに以下のコードを書き込みます。 ボーレートはUnity側で設定した値と同じにします。(例:9600bps)
byte a = 0; void setup() { Serial.begin(9600); } void loop() { Serial.println(a++); delay(500); }
6. 結果
正常に動作していれば、0.5秒間隔で数字がカウントアップしていきます。
7. Tips
今回、動作確認用にArduino Unoを使用しましたが、Mac OS環境でかつArduino Leonardo(ATmega32u4チップ)を使用する場合は、SerialPortクラスを以下のように設定することでUnityのフリーズを防ぐことができます。
serial = new SerialPort(portName, baurate, Parity.None, 8, StopBits.One); serial.Open(); serial.DtrEnable = true; serial.RtsEnable = true; serial.DiscardInBuffer(); serial.ReadTimeout = 5000;// msec
- DtrEnable / RtsEnableのどちらかをEnableにするだけでフリーズは回避できます。
- DiscardInBuffer()することで接続前のバッファをクリアしています。
- ReadTimeoutを設定しておくとSerialPortのトラブルでUnityのプログラム全体がフリーズするのを防ぐことができます。
8. 参考サイト
ENC28J60を使ってESP32でEthernet UDP通信する
MICROCHIPのENC28J60を使ってESP32(Arduino core for the ESP32)でUDP通信を行ってみます。
1. 準備
ENC28J60を入手します。aitendoやAmazonで1,000円程度で購入できます。入手に時間がかかってもよければAliexpressを使うと$2~$3で購入できます。今回はおなじみのチップサイズがSOPではなくSSOPの小さいモジュール版を使用しました。SOP版と同じように使うことができます。
ESP32はESP32-DevKitCを使います。秋月電子で購入できます。
2. 接続
ENC28J60とESP32を以下の図のように接続します。接続情報はこれから使用するEthernetライブラリ(UIPEthernet)のこちらのリンクを参考にしました。
ESP32 | ENC28J60 |
---|---|
IO5 (SS) | CS |
IO23 (VS_MOSI) | SI |
IO19 (VSMISO) | SO |
IO18 (VS_SCK) | SCK |
3V3 | VCC |
GND | GND |
ESP32-DevKitCは3.3Vのレギュレーターに1A以上出力可能なNCP1117を使用しているため、ENC28J60と直接つなげています。とはいえUSBからの給電のため3.3V全体で500mA以上を超えないように気をつける必要はあります。(3.3VラインはESP32のチップへの電源供給にも使われています)
秋月電子電子の商品ページに貼られているリンクからESP32-DevKitCの回路図を確認することができます。
http://akizukidenshi.com/download/ds/espressifsystems/ESP32-Core-Board-V2_sch.pdf
NCP1117データシート
https://www.onsemi.jp/PowerSolutions/document/NCP1117JP-D.PDF
3. ボードライブラリ修正
Arduino core for the ESP32 Ver 1.0.2にはEthernetを使用するためのライブラリに不備があり、コンパイルすることができません。Arduino core for the ESP32 Ver 1.0.1であればビルドが可能になるため、Ver 1.0.1を使う場合はこの項目は飛ばしてください。
Ver 1.0.2でコンパイルできるようにするためにはArduino core for the ESP32のClient.hを修正する必要があります。 Client.hはボードマネージャを使用してインストールしてあれば以下のパスにがあります。(Windows)
C:\Users\<アカウント名>\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.2\cores\esp32\Client.h
テキストエディタでClient.hを開いて以下の行をコメントアウトします。
// virtual int connect(IPAddress ip, uint16_t port, int timeout) =0; // virtual int connect(const char *host, uint16_t port, int timeout) =0;
これに関する経緯については前回の記事の「ライブラリの修正」項目で少し触れているのでそちらを参考ください。 W5500を使ってESP32でEthernet UDP通信する - Re: note
4. Ethernetライブラリのインストール
EthernetライブラリはESP32とENC28J60をサポートしている以下のものを使用します。
Arduino IDEのライブラリマネージャから「UIPEthernet」で検索してインストールすることができます。
ESP32の公式フォーラムESP32 with ENC28J60 Ethernet Module では別のライブラリであるEtherCardからforkされたESP32対応ライブラリも試してみましたが、残念ながらビルドすることができませんでしたので、今回はUIPEthernetを使用して進めます。
5. コード
以下のコードをESP32に書き込みます。
#include <UIPEthernet.h> uint8_t mac[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; uint8_t IP[] = { 10, 0, 1, 8 }; uint8_t MASK[] = { 255, 255, 255, 0 }; uint8_t DNS[] = { 10, 0, 1, 1 }; uint8_t GW[] = { 10, 0, 1, 1 }; EthernetUDP udp; #define UDP_TX_PACKET_MAX_SIZE 700 char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; char ReplyBuffer[] = "acknowledged"; void setup() { Serial.begin(115200); Serial.println("starting..."); // Ethernet.begin(mac);// Use DHCP Ethernet.begin(mac, IP, DNS, GW, MASK); Serial.print("Local IP : "); Serial.println(Ethernet.localIP()); Serial.print("Subnet Mask : "); Serial.println(Ethernet.subnetMask()); Serial.print("Gateway IP : "); Serial.println(Ethernet.gatewayIP()); Serial.print("DNS Server : "); Serial.println(Ethernet.dnsServerIP()); udp.begin(3001); } void loop() { int packetSize = udp.parsePacket(); if (packetSize) { Serial.printf("Received packet of size %d\n", packetSize); IPAddress remote = udp.remoteIP(); Serial.printf("From %d.%d.%d.%d:%d - ", remote[0], remote[1], remote[2], remote[3], udp.remotePort()); udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); Serial.print("Contents: "); Serial.println(packetBuffer); // send a reply to the IP address and port that sent us the packet we received udp.beginPacket(udp.remoteIP(), udp.remotePort()); udp.write(ReplyBuffer); udp.endPacket(); } }
UIPEthernetを使って他の端末からpingを打って応答を確認するだけの場合は、loop()関数の中に
server.available();
だけを入れると良いでしょう。これを書かないとIPを取得できてもpingの応答が返りません。
6. 実行
シリアルモニタでログを確認します。通信相手はNode.jsを使うと簡単に用意することができます。正常に動作していれば、ログに受信したメッセージと相手先にReplyBufferで設定した文字列が表示されます。
動作が不安定だったり動作しなかったりする場合は、電源を別のものに変えたり接続のワイヤーを短くしたりするなどしてみてください。私は最初3.3VラインをESP32からではなく、外部電源から供給していましたがまったく動作しませず、ESP32から直接供給するように変更すると安定動作するようになりました。
7. まとめ
ENC28J60を使ってESP32(Arduino core for the ESP32)でUDP通信を行ってみました。Arduino core for the ESP32は現在でも頻繁に開発が行われており、未完成の部分や不安定な部分がありますが、ESP32は他のマイコンと比較すると入手性も良く、安価・高性能と大きなポテンシャルを持っています。これからも多くの人に使われることによってライブラリも洗練されると嬉しいですね。
W5500を使ってESP32でEthernet MQTT通信する
WIZnetのW5500を使ってESP32(Arduino core for the ESP32)でMQTT通信を行ってみます。
1. 準備
過去の記事を参考に3までを行います。
- 1.準備
- 2.接続
- 3.ライブラリ修正
2. MQTTライブラリのインストール
MQTT通信するために以下のライブラリをインストールします。
ライブラリはArduino IDEのライブラリマネージャから名前で検索してインストールすることができます。
PubSubClientについてはAPIドキュメントが用意されています。
3. コード
以下のコードをESP32に書き込みます。
#include <Ethernet.h> #include <PubSubClient.h> byte mac[] = { 0x70, 0x69, 0x69, 0x2D, 0x30, 0x31 }; byte server[] = { 10, 0, 1, 5 }; void callback(char* topic, byte* payload, unsigned int length) { payload[length] = '\0'; String msg = String((char*) payload); Serial.println(msg); } EthernetClient ethClient; PubSubClient client(server, 1883, callback, ethClient); void reconnect() { while (!client.connected()) { Serial.println("Attempting MQTT connection..."); if (client.connect("myClient")) { Serial.println("MQTT PubSub Ready"); client.publish("output", "ready"); client.subscribe("input"); Serial.println("subscribe input"); } else { Serial.println("MQTT PubSub failer"); Serial.println("try again in 2 seconds"); delay(2000); } } } void setup() { Serial.begin(115200); Serial.println("starting..."); Ethernet.init(33);// CS pin 33 Ethernet.begin(mac);// use DHCP // Check for Ethernet hardware present if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); while (true) { delay(1); // do nothing, no point running without Ethernet hardware } } if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected."); } Serial.print("IP: "); Serial.println(Ethernet.localIP()); delay(1500); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); }
MACアドレスは環境に合わせて変更してください。IPアドレスの設定はDHCPを使っていますが、Ethernet.begin()の引数にIPアドレスやgatewayを指定することで固定IPを設定することもできます。
固定IPで設定する際のdns・gateway・subnetはデフォルト値が設定されているため、MACアドレスとIPアドレスだけで動作する場合がありますが、指定しないと動作しなかったり通信が不安定だったりするので面倒でも全て指定した方が良いでしょう。Ethernet.begin()の詳細は以下の公式リファレンスから確認できます。
またserverアドレスはMQTT Brokerのアドレスで、動作確認するためにはMQTT Brokerが必要になります。MQTT BrokerはRaspberry Piにmosqittoをインストールして構築するのが一番簡単でおすすめです。
4. 実行
MQTT Brokerが動作していることが確認できたら、ESP32に繋がっているシリアルモニタでログを確認します。正常に動作していれば以下のようになります。
mosquittoをインストールすると、Brokerの他にPubSubクライアントもインストールされるのでPublish(Pub)用とSubscribe(Sub)用のターミナルを立ち上げて動作を確認してみます。
トピックはoutputをSubscribeしておきます。ESP32が立ち上がるとreadyが送られてくるのを確認できます。見逃してしまったらESP32をリセットするともう一度送られてきます。
inputトピックに対してメッセージ(payload)を送るとシリアルモニタで受信を確認することができます。
5. まとめ
ESP32でEthernetを使ったMQTT通信を行ってみました。ESP32にはWiFiが搭載されているため無線通信の方が気軽に試すことができますが、無線では得られない圧倒的な通信安定度がEthernetにはありますので用途によって使い分けられると良いかと思います。
W5500を使ってESP32でEthernet UDP通信する
WIZnetのW5500を使ってESP32(Arduino core for the ESP32)でUDP通信を行ってみます。
1. 準備
Aliexpress等でW5500を購入します。
1 個 USR ES1 W5500 チップ新 spi 蘭イーサネット変換 TCPIP Mod - Aliexpress.com | Alibaba グループ上の 電子部品&用品 からの 集積回路 の中
ESP32はESP32-DevKitCを使います。秋月電子で購入できます。
2. 接続
W5500とESP32を以下のように接続します。3.3Vの供給についてはW5500の消費電流が大きいためESP32から行わずに外部から加えるようにしています。
(2019.06.23追記)
ESP32-DevKitCであれば3.3Vのレギュレーターに1A以上出力可能なNCP1117を使用しているため、W5500と直接つなげても駆動できるかと思います。
ESP32 | W5500 |
---|---|
IO33 | CS |
IO23 (VS_MOSI) | MOSI |
IO19 (VSMISO) | MISO |
IO18 (VS_SCK) | SCLK |
3V3 | VCC |
GND | GND |
データシートを見ると100M Trasmittingのときに132mA消費すると書いてあるので、外部電源は200mA以上供給できるものを用意すると良いでしょう。
products:w5500:datasheet [Document Wiki]
3. ライブラリ修正
配線が終わったらコードの書き込みに進みたいところですが、Arduino core for the ESP32 Ver 1.0.2にはEthernetを使用するためのライブラリに不備があり、コンパイルすることができません。
コンパイルできるようにするためにはArduino core for the ESP32のClient.hを修正する必要があります。 Client.hはボードマネージャを使用してインストールしてあれば以下のパスにがあります。(Windows)
C:\Users\<アカウント名>\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.2\cores\esp32\Client.h
テキストエディタでClient.hを開いて以下の行をコメントアウトします。
// virtual int connect(IPAddress ip, uint16_t port, int timeout) =0; // virtual int connect(const char *host, uint16_t port, int timeout) =0;
このあたりの事情は以下のやり取りで伺い知ることができます。
- Adding ESP32 support and fix for ESP32 v 1.0.2 by kmpelectronics · Pull Request #95 · arduino-libraries/Ethernet · GitHub
- compilation error with ESP32 DIV Kit · Issue #97 · arduino-libraries/Ethernet · GitHub
- Server.h not matching Arduino API · Issue #2704 · espressif/arduino-esp32 · GitHub
- Add connect with timeout to Client class · espressif/arduino-esp32@9a9ff62 · GitHub
また、この事象によって報告されている不具合
- Problem with Ethernet.h compilation after board library update from 1.0.1 to 1.0.2 · Issue #2786 · espressif/arduino-esp32 · GitHub
- M5StackでEthernetが使えない問題 - Qiita
- Virtual functions in client.h are out of line with standard Arduino API · Issue #2755 · espressif/arduino-esp32 · GitHub
要約するとArduino core for the ESP32 Ver 1.0.2ではArduinoのEthernet.hとの互換性を意識していない実装になっているためコンパイルエラーが発生しているようです。
ここではJuraj Andrássy氏の意図を汲んで、Arduino側のライブラリを修正するのではなくArduino core for the ESP32側のライブラリを修正して進めます。
4. コード
ライブラリを修正したら、以下のコードをESP32に書き込みます。
#include <Ethernet.h> #include <EthernetUdp.h> byte mac[] = { 0x70, 0x69, 0x69, 0x2D, 0x30, 0x31 }; unsigned int localPort = 3001; char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; char ReplyBuffer[] = "acknowledged"; EthernetUDP Udp; void setup() { Serial.begin(115200); Serial.println("starting..."); Ethernet.init(33);// CS pin 33 Ethernet.begin(mac);// use DHCP // Check for Ethernet hardware present if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); while (true) { delay(1); // do nothing, no point running without Ethernet hardware } } if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected."); } Serial.print("IP: "); Serial.println(Ethernet.localIP()); Udp.begin(localPort); } void loop() { // if there's data available, read a packet int packetSize = Udp.parsePacket(); if (packetSize) { Serial.print("Received packet of size "); Serial.println(packetSize); Serial.print("From "); IPAddress remote = Udp.remoteIP(); for (int i=0; i < 4; i++) { Serial.print(remote[i], DEC); if (i < 3) { Serial.print("."); } } Serial.print(", port "); Serial.println(Udp.remotePort()); // read the packet into packetBufffer Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); Serial.print("Contents: "); Serial.println(packetBuffer); // send a reply to the IP address and port that sent us the packet we received Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); Udp.write(ReplyBuffer); Udp.endPacket(); } delay(10); }
MACアドレスは環境に合わせて変更してください。IPアドレスの設定はDHCPを使っていますが、Ethernet.begin()の引数にIPアドレスやgatewayを指定することで固定IPを設定することもできます。
固定IPで設定する際のdns・gateway・subnetはデフォルト値が設定されているため、MACアドレスとIPアドレスだけで動作する場合がありますが、指定しないと動作しなかったり通信が不安定だったりするため、面倒でも全て指定した方が良いでしょう。Ethernet.begin()の詳細は以下の公式リファレンスから確認できます。
5. 実行
シリアルモニタでログを確認します。通信相手はNode.jsを使うと簡単に用意することができます。正常に動作していれば、ログに受信したメッセージと相手先にReplyBufferで設定した文字列が表示されます。
6. 参考リンク
W5500を使ってArduinoでMQTT通信する
WIZnetのW5500を使ってArduinoでMQTT通信を行ってみます。
1. 準備
Aliexpress等でW5500を購入します。
1 個 USR ES1 W5500 チップ新 spi 蘭イーサネット変換 TCPIP Mod - Aliexpress.com | Alibaba グループ上の 電子部品&用品 からの 集積回路 の中
ArduinoはUNO R3を使います。
2. 接続
ArduinoとはSPIで通信を行うため、W5500を以下のように接続します。IOレベルは3.3Vですが5VトレラントでもあるのでArduinoへ直結することができます。その他の注意点としてはW5500の消費電流が大きいため3.3Vの供給をArduinoから行わずに外部から加えるようにしています。
Arduino | W5500 |
---|---|
D10 (SS) | CS |
D11 (MOSI) | MOSI |
D12 (MISO) | MISO |
D13 (SCK) | SCLK |
GND | GND |
データシートを見ると100M Trasmittingのときに132mA消費すると書いてあるので、外部電源は200mA以上供給できるものを用意すると良いでしょう。
products:w5500:datasheet [Document Wiki]
【注意】ブレッドボードで接続する場合はワイヤーをなるべく短くしてください。ワイヤーが長いと通信が不安定になります。
3. ライブラリのインストール
W5500を使用する場合はENC28J60とは異なり、標準のEthernetライブラリでモジュールを制御することができるため、MQTT部分を処理するPubSubClientのみをインストールします。
ライブラリはArduino IDEのライブラリマネージャから名前で検索してインストールすることができます。
4. コード
以下のコードをArduinoに書き込みます。
#include <Ethernet.h> #include <PubSubClient.h> byte mac[] = { 0x70, 0x69, 0x69, 0x2D, 0x30, 0x31 }; byte server[] = { 10, 0, 1, 5 }; void callback(char* topic, byte* payload, unsigned int length) { payload[length] = '\0'; String msg = String((char*) payload); Serial.println(msg); } EthernetClient ethClient; PubSubClient client(server, 1883, callback, ethClient); void reconnect() { while (!client.connected()) { Serial.println("Attempting MQTT connection..."); if (client.connect("myClient")) { Serial.println("MQTT PubSub Ready"); client.publish("output", "ready"); client.subscribe("input"); Serial.println("subscribe input"); } else { Serial.println("MQTT PubSub failer"); Serial.println("try again in 2 seconds"); delay(2000); } } } void setup() { Serial.begin(9600); Serial.println("starting..."); Ethernet.init(10);// CS pin 10 Ethernet.begin(mac); // Check for Ethernet hardware present if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); while (true) { delay(1); // do nothing, no point running without Ethernet hardware } } if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected."); } Serial.print("IP: "); Serial.println(Ethernet.localIP()); delay(1500); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); }
PubSubClientにはAPIドキュメントも用意されています。
MACアドレスやserverアドレスは環境に合わせて変更してください。 serverアドレスはMQTT Brokerのアドレスで、動作確認するためにはMQTT Brokerが必要になります。MQTT BrokerはRaspberry Piにmosqittoをインストールして構築するのが一番簡単でおすすめです。
5. 実行
MQTT Brokerが動作していることが確認できたら、Arduinoに繋がっているシリアルモニタでログを確認します。正常に動作していれば以下のようになります。
mosquittoをインストールすると、Brokerの他にPubSubクライアントもインストールされるのでPublish(Pub)用とSubscribe(Sub)用のターミナルを立ち上げて動作を確認してみます。
トピックはoutputをSubscribeしておきます。Arduinoが立ち上がるとreadyが送られてくるのを確認できます。見逃してしまったらArduinoをリセットするともう一度送られてきます。
inputトピックに対してメッセージ(payload)を送るとArduinoのシリアルモニタで受信を確認することができます。
6. まとめ
W5500やENC28J60を使うことで非力なマイコンでもTCPやUDP通信できることをこのブログで紹介してきました。最近ではWi-Fiを搭載したESP8266やESP32の登場により、単体で気軽にIoT機器を作ることができるようになりましたが、人が多い場所や電波干渉が顕著な環境では有線による安定動作が望まれる場面もあるかと思います。
マイコンで通信を行うときはコストや用途、使用環境などを考慮して有線や無線、プロトコルを選択できるようになると良いでしょう。
ENC28J60を使ってArduinoでMQTT通信する
MICROCHIPのENC28J60を使ってArduinoでMQTT通信を行ってみます。
1. 準備
ENC28J60を入手します。aitendoやAmazonで1,000円程度で購入できます。入手に時間がかかってもよければAliexpressを使うと$2~$3で購入できます。
ArduinoはUNO R3を使います。
2. 接続
ArduinoとはSPIで通信を行うため、ENC28J60を以下のように接続します。IOレベルは3.3Vですが5VトレラントでもあるのでArduinoへ直結することができます。その他の注意点としてはENC28J60の消費電流が大きいため3.3Vの供給をArduinoから行わずに外部から加えるようにしています。このあたりはWIZnetのW5500と同じですね。
Arduino | ENC28J60 |
---|---|
D10 (SS) | CS |
D11 (MOSI) | SI |
D12 (MISO) | SO |
D13 (SCK) | SCK |
GND | GND |
ENC28J60のモジュールによってはピン配置が異なるため、実物の仕様やシルク等を確認して割り当てられたピンと適切に接続してください。今回購入したENC28J60のモジュールでは以下のようになっていました。
また、ENC28J60のデータシートを見ると動作時の最大消費電流が180mAとあるので、少なくとも200mA以上の供給能力がある外部電源を用意してください。
ENC28J60 - Ethernet Controllers
(2019.06.15 追記)
ブレッドボードで接続する場合はワイヤーをなるべく短くしてください。ワイヤーが長いと通信が不安定になります。
3. ライブラリのインストール
以下の2つのライブラリを使用します。
UIPEthernetはENC28J60でEthernet通信するためのライブラリで、PubSubClientはMQTT通信するためのライブラリです。PubSubClientについてはENC28J60をサポートしていないようですが、動作確認することができたのでこのまま進めます。
ライブラリは2つともArduino IDEのライブラリマネージャから名前で検索してインストールすることができます。
4. コード
以下のコードをArduinoに書き込みます。
#include <UIPEthernet.h> #include <PubSubClient.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; byte server[] = { 10, 0, 1, 9 }; EthernetClient ethClient; void callback(char* topic, byte* palyload, unsigned int length); PubSubClient client(server, 1883, callback, ethClient); void setup() { Serial.begin(9600); Serial.println("starting..."); Ethernet.begin(mac); Serial.print("IP Address: "); Serial.print(Ethernet.localIP()); Serial.print("/"); Serial.println(Ethernet.subnetMask()); while (!client.connected()) { if (client.connect("myClient")) { Serial.println("MQTT PubSub Ready"); client.publish("output", "ready"); client.subscribe("input"); Serial.println("subscribe input"); } else { Serial.println("MQTT PubSub failer"); Serial.println("try again in 2 seconds"); delay(2000); } } } void loop() { client.loop(); } void callback(char* topic, byte* payload, unsigned int length) { payload[length] = '\0'; String msg = String((char*) payload); Serial.println(msg); }
PubSubClientにはAPIドキュメントも用意されています。
MACアドレスやserverアドレスは環境に合わせて変更してください。 serverアドレスはMQTT Brokerのアドレスで、動作確認するためにはMQTT Brokerが必要になります。MQTT BrokerはRaspberry Piにmosqittoをインストールして構築するのが一番簡単でおすすめです。
5. 実行
MQTT Brokerが動作していることが確認できたら、Arduinoに繋がっているシリアルモニタでログを確認します。正常に動作していれば以下のようになります。
mosquittoをインストールすると、Brokerの他にPubSubクライアントもインストールされるのでPublish(Pub)用とSubscribe(Sub)用のターミナルを立ち上げて動作を確認してみます。
トピックはoutputをSubscribeしておきます。Arduinoが立ち上がるとreadyが送られてくるのを確認できます。見逃してしまったらArduinoをリセットするともう一度送られてきます。
inputトピックに対してメッセージ(payload)を送るとArduinoのシリアルモニタで受信を確認することができます。
6. まとめ
ENC28J60を使ってArduinoでMQTT通信できることが確認できました。しかしながら、PubSubClientがENC28J60のサポートを正式に謳っていないことや、スケッチを書き込んでみると分かりますがArduino UNOを使うとフラッシュメモリを70%以上消費してしまうため、使用にはやや不安が残ります。
これらの点に注意しながら使うか、別のイーサネットモジュール(W5500など)や別のライブラリの使用を検討するのも良いかと思います。
7. 参考リンク
ENC28J60を使ってArduinoでUDP通信する
MICROCHIPのENC28J60を使ってArduinoでUDP通信を行ってみます。
1. 準備
ENC28J60を入手します。aitendoやAmazonで1,000円程度で購入できます。入手に時間がかかってもよければAliexpressを使うと$2~$3で購入できます。
ArduinoはUNO R3を使います。
2. 接続
ArduinoとはSPIで通信を行うため、ENC28J60を以下のように接続します。IOレベルは3.3Vですが5VトレラントでもあるのでArduinoへ直結することができます。その他の注意点としてはENC28J60の消費電流が大きいため3.3Vの供給をArduinoから行わずに外部から加えるようにしています。このあたりはWIZnetのW5500と同じですね。
Arduino | ENC28J60 |
---|---|
D10 (SS) | CS |
D11 (MOSI) | SI |
D12 (MISO) | SO |
D13 (SCK) | SCK |
GND | GND |
ENC28J60のモジュールによってはピン配置が異なるため、実物の仕様やシルク等を確認して割り当てられたピンと適切に接続してください。今回購入したENC28J60のモジュールでは以下のようになっていました。
また、ENC28J60のデータシートを見ると動作時の最大消費電流が180mAとあるので、少なくとも200mA以上の供給能力がある外部電源を用意してください。
https://www.microchip.com/wwwproducts/en/en022889
(2019.06.15 追記)
ブレッドボードで接続する場合はワイヤーをなるべく短くしてください。ワイヤーが長いと通信が不安定になります。
3. コード
W5500であれば標準ライブラリのEthernet.hが使用できますが、ENC28J60は対応していないようなので、今回は別ライブラリのEtherCardを使用します。Arduino Library Managerに対応しているので、Arduino IDEのライブラリマネージャからインストールすることができます。(「ethercard」で検索すると出てきます)
他にもENC28J60に対応しているライブラリとしてはUIPEthernetもあります。
以下のコードをArduinoに書き込みます。
#include <EtherCard.h> const byte ip[] = { 10, 0, 1, 10 }; const byte gw[] = { 10, 0, 1, 1 }; const byte dns[] = { 10, 0, 1, 1 }; const byte mac[] = { 0x1A, 0x2B, 0x3C, 0x4D, 0x5E, 0x6F }; byte Ethernet::buffer[700]; void udpSerialPrint(uint16_t dest_port, uint8_t src_ip[IP_LEN], uint16_t src_port, const char *data, uint16_t len){ Serial.print("dest_port: "); Serial.println(dest_port); Serial.print("src_port: "); Serial.println(src_port); Serial.print("src_ip: "); ether.printIp(src_ip); Serial.print("\ndata: "); Serial.println(data); ether.sendUdp(data, len, dest_port, src_ip, src_port);// send echo back } void setup () { Serial.begin(9600); Serial.println("starting..."); if (ether.begin(sizeof Ethernet::buffer, mac, SS) == 0) { Serial.println( "Failed to access Ethernet controller"); } ether.staticSetup(ip, gw, dns); ether.printIp("IP: ", ether.myip); ether.printIp("GW: ", ether.gwip); ether.printIp("DNS: ", ether.dnsip); ether.udpServerListenOnPort(&udpSerialPrint, 1337); } void loop () { ether.packetLoop(ether.packetReceive()); }
MACアドレスやIPアドレスは環境に合わせて変更してください。 EtherCardの詳細なリファレンスも用意されています。
4. 実行
シリアルモニタでログを確認します。通信相手はNode.jsを使うと簡単に用意することができます。正常に動作していれば、ログに受信したメッセージと相手先に同じメッセージをエコーバックしていることが確認できます。