「#4-5 WiFiインターネット通信」では、ESP32を使って、WiFiインターネットアクセスを試しました。実は、ESP32が思ったように機能しなかった場合に備え、別なネットワーク接続モジュールを購入していました。それが、ENC28J60モジュールです。
ESP32が期待通りに使えそうな感触を得ているので、ENC28J60は不要になってしまったのですが、せっかく手元にあるので、使ってみようと思います。
MicrochipのENC28J60(型番)というチップの載ったモジュールです。データシートを検索してみましょう。
ENC28J60のデータシートを検索(別ウインドウが開きます)
特徴は以下の通り。
検索したのは、チップ単体のデータシートです。購入したのは、もろもろ必要な回路が構成されて、MCUとのインターフェイス信号だけが端子に引き出された以下図の様なモジュールです。
同じENC28J60が載っていて、見た目もほとんど同じなのに、ボード上に5V→3.3Vレギュレータが載ったバージョンもあるようです。本HPで購入したモジュールには、レギュレータが載っていません。従って、3.3V電源を供給する必要があります。
この3.3V電源ですが、Arduinoの3.3V出力を繋いではいけません。
手持ちのELEGOO UNO互換機のデータシートを見ると、3.3V電源から引き出せる電流は、最大50mAとなっていました。たぶん、本家UNOや、他の互換機でも、似たような値でしょう。一方、ENC28J60は、「TYPで160mA流れる」と、データシートに記載されているので、電流容量が足りません。
別途、ENC28J60を支えられるだけの電源を供給してやる必要があります。以下図の様な電源モジュールから3.3V電源を確保しました。
Arduinoとの接続はSPIですので、回路図は以下の様になります。
ENC28J60は3.3V定格のチップですので、5VのArduinoと接続する時には、本来であれば、レベルシフタが必要となります。ただし、ENC28J60の入力端子は5Vトレラントなので、Arduinoの5V信号を直結できます。よって、出力信号だけ、レベルシフタを通しています。
「釈迦に説法」かもしれませんが、一応、レベルシフタの必要性について議論しておきます。
上で示した回路ですが、レベルシフタなしで、ENC28J60.SO→Arduino.D12直結でも、たぶん、つながって、動きます(保証はありませんが……)。
スペックを確認しておきましょう。
Vdd=3.3V (TYP)、Vcc=5V (TYP)で、考えてみましょう。
と、言うことで、スペック面では、つながりません。
ここで、ENCのスペックに規定された、Ioh=4.0mAの条件が効いてきます。これは、「HIGHを出力する時、電流負荷が4.0mAだった場合に、VohがVdd−0.7Vまでドロップする」ということを指しています。
もし、Ioh≒0mAだったら、HIGHレベルの駆動は、電源電圧Vdd付近まで上昇するはずですので、Voh≒Vddと考えることができます。その場合には、3.0V (ATmega)≦Vdd(=3.3V) (ENC)が成立するため、問題なく接続可能となります。
普通、MCUと信号線を直結しただけなら、Ioh≒0mAでしょうから、それを自分で保証できるのなら、OKと言うことです。各自の保証となります。
※もし、本当に0mA<Ioh≦4.0mAの電流負荷がついているのなら、うまく通信できない場合がありますので、設計は慎重かつ確実に。
将来、5Vトレラントになっていない3.3Vチップとのインターフェイスには、確実に必要になるので、以下の様な4chの双方向レベルシフタモジュールを購入しました。
例えば、「#4-5 WiFiインターネット通信」で使ったESP32は、5Vトレラントになっていないので、将来、Arduinoと接続して制御する場合には、レベルシフタが必要となります。
※購入したモジュールは、プッシュプルタイプのドライバではなく、オープンドレイン+プルアップタイプになっているようです。よって、出力側にあまり大きな容量負荷が付くと、信号変化が鈍って、信号遅延が大きくなる恐れがあります。SPIは8MHz(ATmega328 @16MHzの最速設定)で通信する、結構高速なインターフェイスなので、スピードで引っかかるかも??レベルで苦労するか、スピードで苦労するか、……究極の選択?
ちょっと残念なことに、購入したENC28J60モジュールには、MACアドレスがついていませんでした。本家Arduinoのネットワーク拡張ボードには、シールか何かで、MACアドレスがついているのだそう。
ネットワークに接続するのに、MACアドレスが必要になります。しかも、MACアドレスはネットワーク内でユニークでなければなりません。つまり、他の機器と重複してはならない。
従って、このモジュールを使うためには、各自、MACアドレスを何とかしないとなりません。
今回は、筆者宅の家庭内LAN内で閉じて実験することにして、LAN内のすべての機器と重複しないMACアドレスを割り当てて実験します。各自、同様にMACアドレスを準備しないと、このモジュールでネットワークに接続できない事に注意してください。
ネットワーク内全機器のMACアドレスを把握している必要があるので、全体を把握できない企業内ネットワークや、もっと大きなインターネットなんかにうっかり繋いでしまわないように注意してください。
後ほどリンクする完成版プログラムは、MACアドレス部分にエラーが注入してあり、各自適切な値を設定しないとコンパイルが通らないようになっています。
さすがに、ENC28J60のドライバから自前で起こすのはしんどいので、そこは、Arduino用にリリースされているライブラリの中からどれかを使わせてもらおうと思います。
Arduino IDEを起動して、「ツールメニュー ⇒ ライブラリを管理」を選択します。「ライブラリマネージャ」が開きますので、検索フィールドに「enc28j60」と打って、ライブラリを検索してみました。いくつか表示された候補の「More info」をクリックして精査した結果、UIPEthernetと言うライブラリを使うことに決めました。決め手は以下:
ライブラリマネージャでインストールしました。
順番が逆になりますが、この先、UIPEthernetライブラリを使ってプログラムを組んだところ、FlashもRAMも、メモリがひどく圧迫されていることに気が付きました。
まず、一番単純なプログラムサンプル「Blink」に、以下の行だけを追加して、前後のコンパイル結果を比較してみます。
#include <UIPEthernet.h>
着目点は、メモリの使用量です(欠点)。
Flash容量 | RAM容量 | |
サンプルBlink | 930バイト | 9バイト |
Blink+#include <UIPEthernet.h> | 11,842バイト | 1,013バイト |
Arduino UNO (ATmega328)のメモリ | 32,768バイト | 2,048バイト |
アラアラ、すごい。こんなに?RAMに至っては、すでに半分使ってしまっています。ただ#includeしただけで、まだ何もネットワーク関連のプログラムは組んでいないのに、です。
ライブラリの物量に圧迫されて、作りたいアプリケーションが満足に組めないかも?これは、要注意ですね。
今回は、「TELNET(もどき?)サーバ」(←命名)を作ってみたいと思います。TELNETのホスト側プログラムです。
TELNETも、IETFのRFCで規定されています。まずは、TELNETのRFCを検索しておきましょう。RFCって?IETFって?と言う方は、「#4-5 WiFiインターネット通信」を復習してみてください。
TELNETのRFCドキュメントを検索する(別ウインドウが開きます)
では、「TELNET(もどき?)サーバ」の目標仕様を記しておきます。
※RFC-854をざっと斜め読みしただけなので、多少解釈や実装が間違っていても、ご愛敬です(1^^;)。もし、「TELNET規格準拠」を謳おうとするアプリケーションを作成する場合には、各自RFCを確認して適宜修正してください。
今までの様にUSB⇔UART変換を使った操作・制御では、USBケーブルの届く範囲にホストPCが必要でした。ネットワーク(Ethernet)のTELNETで操作・制御できるようになると、この呪縛から解き放たれ、一気に可能性が広がります。
では、コーディングに進みましょう。
// telnet server
uint8 mac[ ]={ 0xYZ, 0xYZ, 0xYZ, 0xYZ, 0xYZ, 0xYZ };
uint8 ip[ ]={ 192, 168, 0, 50 };
EthernetServer server_23=EthernetServer (telnet_PORT);
EthernetClient client_23;
MACアドレスを保持する配列(mac[ ])ですが、"0xYZ"は数値として不適切なので、コンパイルエラーになります。各自、「uint8 mac[ ]={ … };」の部分に、有効なMACアドレスを指定してください。
このライブラリ(UIPEthernet)、DHCPに対応しているのかどうか知りませんが、アドレスが固定できないと、TELNETでPCから接続する時に面倒なので、IPアドレスを"192.168.0.50"に固定してあります。各自のネットワーク環境に合わせて書き換えてください。
※試しに、IPアドレスを指定せずに、「Ethernet.begin (mac);」として見たところ、DHCPからIPアドレスを取得して、ネットワークに接続していました。素晴らしい。ただ、本文でも言いましたが、一々、DHCPが割り当てたIPアドレスを確認して接続するのは面倒なので、上コードの様に固定しておいた方が、実験は楽です。
ポート名は、TELNETを表す23(=telnet_PORT)に設定してありますが、別なポートを使う場合は、これも書き換えてください。
以下、setup()関数内での初期化項目です。
次に、loop()関数内での処理を記述します。ただ、筆者のTELNETに対する理解があいまいなだけに、細かな処理内容について解説を始めても、あまり意味が無いかと……。よって、RFCとプログラムを見比べながら、各自確認してみてください。
ここでは、クライアント(端末)から送信されてきたデータの受信方法と、サーバ(Arduino)からデータを送信する方法についてのみ、解説しておきます。
では、完成版プログラムをリンクしておきます。
完成版プログラム:telnet_server_enc.ino
通信端末(TELNETクライアント)ソフトウェアも限定しておきます。本ページでは、Windows版のTera Termと言うソフトウェアを使いました。
Tera Termをベクターで検索する(別ウインドウが開きます)
予め、ダウンロードしてインストールまで済ませておいてください。
最初に示した回路図に従って、Arduino+ENC28J60(+レベルシフタ)+電源モジュールの回路を構成し、USBケーブル、ACアダプタ、Etherケーブルを接続してください。
完成版プログラムをArduinoへコンパイル&書き込みします。そうしたら、シリアルモニタを開いてください。
今回、Arduinoの制御はTELNET側で行われるので、シリアルモニタ側はデバッグ情報が一方的に送信されてきます。
それでは、Tera Termを起動して、「Fileメニュー ⇒ New Connection...」を実行します。
上図の様に設定(必要に応じてIPアドレスとポート名を変更)して、「OK」ボタンを押します。
Arduino側含め、ネットワークの構成など、全てが正しく構築されていれば、真っ新なウインドウが開いて、Connectされます。どこかに間違いがあると、ここで「接続できない」旨のエラーが発生するかもしれません。
特にプロンプトを出力していないので、真っ白なんですが(1^^;)、Tera Termで「Controlメニュー ⇒ Are you there」を実行した時に、Arduinoから"> "というプロンプトが飛んできたら、接続できています。通常、プロンプトは表示しませんが、常にコマンドは受け付けているので、以下の様に実験してみてください。
実験サンプル:【 】内はTera Termからの送信データ
【who】
OK: telnet_server_enc ver.1.00
【help】
OK+ who
OK: help
いつも、本HPのプログラムで、シリアルモニタに対して実行していたコマンドを、TELNET経由で実行することができました。つまり、TELNET(ネットワーク)経由でArduinoを遠隔操作・制御可能になった、と言うことです。
尚、シリアルモニタの方には、デバッグ情報が表示されています(telnet_server_enc.log)。Tera Termから、どのようなコマンド(ネゴシエーション)が飛んできているか?確認しておいてください。
今回のお買い物は、以下の通りです。
しかし、ライブラリのメモリ喰らいは、想定外でした。こっち(ENC28J60)の有線ネットワークモジュールは、無線ネットワークモジュール(ESP32)が思い通りに使いこなせなかった場合の予備として購入しておいたのですが、ESP32が期待通りに使えそうな感触を得ているので、やはり、メインで使うのはESP32になりそうです。