今回は1個約400円の激安パチモノArduino nanoと、同じく約350円のEtherアダプタENC28J60をSPIで接続しました。
サーバ側の記事とクライアントの記事(この記事)に分けました。サーバ側についてはこちらの記事をご覧下さい。
最初に注意事項です。ENC28J60は3.3v電源で動作しますが、Arduinoから外部に供給可能な3.3vの電流容量は50mAまでです。ENC28J60は最大で250mA程度消費するとのことなので、ぜんぜん足りません。最初、それに気づかなくて、「なんでEtherのランプが点灯しないんだろ…壊れたか?」と思ってました。
というわけで、9VのACアダプタからArduinoへ電源を供給しましたが、手持ちで250mAを供給できる3.3vレギュレータは秋月で買った超高能率三端子レギュレータ、380円。本体よりも電源の方が高くなって1000円を超えてしまいました。ただ、普通のシリーズレギュレータなら3.3v1Aで100円ぐらいなので…。
電源はACアダプタの9vをブレッドボード手前のレールに接続し、Arduino nanoのVINとGNDに供給しています。同じく手前の9vレールから三端子レギュレータのVin/GNDに接続し、GND/Voutをブレッドボード奥のレールに接続しています。Arduino nanoの5vと3.3v出力はどこにも接続していません。
■接続など■
Arduinoとの接続はSPIです。ENC28J60ピン | Arduino nano/unoピン |
VCC | 3.3v電源 |
GND | GND |
CS | 8 |
RST | Reset(省略可) |
SI | 11 |
SCK | 13 |
SO | 12 |
別のArduinoと接続する場合には、SO=MISO, SI=MOSIと読み替えてください。また、前述の通り、3.3vはArduino出力ではなく電源アダプタ〜三端子レギュレータからの出力です。三端子レギュレータの出力側には手持ちの関係で1000μF 35vの電解コンデンサと0.1μFの積層セラミックコンデンサを入れましたが、電解コンデンサは100μF 25v程度で問題ないかと思います。
今回はArduinoに温湿度センサーHDC1000と気圧センサーMPL115A2をI2cで接続しました。
HDC1000(秋月) | MPL115A2(秋月) | Arduino nano/uno |
VCC(1) | VDD(1) | 3.3v |
SDA(2) | SDA(7) | A4 |
SCL(3) | SCL(8) | A5 |
RDY(4) | 2 | |
GND(5) | GND(3) | GND |
CAP(2)とGNDの間に1μFを入れる |
上記HDC1000/MPL115A2のピン番号は秋月版モジュールのピン番号です。HDC1000のRDY端子はArduinoの2につないでいますが、これは今回使用したライブラリのデフォルトが2だからです。HDC1000 mySensor(0x40, 4); の2つめのパラメータを変更すれば他の端子でも使えます。
MPL115A2のCAP端子とGNDの間には1μFのコンデンサを接続します。これを入れないと動作(測定値)がものすごく不安定になります。その辺の経緯はこの辺の記事をご参照くださいませ(涙)。I2cのプルアップ抵抗はHDC1000に入っているので不要です。
■ライブラリ■
まず最初に。各ライブラリ制作者の貢献に敬意と感謝を表します。使用したENC26J60, HDC1000, MPL115A2用ライブラリは下記の通りです。モジュール | ライブラリ |
HDC1000 | https://github.com/ftruzzi/HDC1000-Arduino |
MPL115A2 | https://github.com/adafruit/Adafruit_MPL115A2 |
ENC28J60 | https://github.com/jcw/ethercard |
ENC26J60用としてはいくつか公開されています。一応一通り試してみたのですが、今回は「EtherCard」を用います。機能が多いのにRAMのフットプリントが小さくてArduino nanoでも余裕があるからです。ただ、ライブラリにありがちなことですが、必要最小限の資料しかないので「サンプルは動くけど、自前のコードが動かなくて頭を抱える」パターンでしばらく悩みました。
Adafruitさん、MPL1115A2を秋月で買ったのにライブラリだけ拝借させていただいてすみません。送料がかかるので、もうちょっとショッピングカートに欲しいものが貯まったときに注文します。
HDC1000はググって落として一発で動いたがこれということで他は試していません。
■ソース■
EtherCard収録のサンプルwebClientをベースにhttp postで動くようにしてHDC1000とMPL115の処理を追加したものです。なお、行番号69/70にdhcpSetupを呼び出しています。本来ならばこれはそのあとの#if USE_DHCPの中に入れるべきなのですが、ここで何らかの初期化が行われているようで、非DHCPで使いたい場合もこれを呼び出さないと動いてくれません。接続相手はポートとIPアドレスで指定します。IPアドレスは24〜29あたり、ポートは85のようにether.hisportに指定します。今回、JSON形式でBeaglebone black上のアプリにPOSTしています。サーバ側についてはこちらの記事をご参照ください。
以下のソースについては、権利も義務もすべて放棄します。ただ、各ライブラリのLicenseについてはご注意ください。特にethercardはGPLです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <EtherCard.h> | |
#include <Wire.h> | |
#include <HDC1000.h> | |
#include <Adafruit_MPL115A2.h> | |
#define USE_DHCP 0 | |
#define USE_DNS 0 | |
// | |
// Ethernet configratios | |
// | |
byte Ethernet::buffer[700]; | |
static byte MyMAC[] = { 0x20,0x15,0x08,0x15,0x17,0x05 }; | |
#if USE_DHCP | |
#else | |
const uint8_t GWIP[] = {192, 168, 0, 1}; | |
const uint8_t MyIP[] = {192, 168, 0, 42}; | |
#endif | |
#if USE_DNS | |
const char HisName[] = "foo.bar.com"; | |
#else | |
const char HisName[] = "192.168.0.72"; | |
const uint8_t HisIP[] = {192, 168, 0, 72}; | |
#endif | |
static void etherCallback (byte status, word off, word len) { | |
// Serial.println(">>>"); | |
// Ethernet::buffer[off+300] = 0; | |
// Serial.print((const char*) Ethernet::buffer + off); | |
// Serial.println("..."); | |
Serial.println("sent."); | |
} | |
// | |
// HDC1000 | |
// | |
HDC1000 hdc1000(0x40, 4); | |
// | |
// MPL115A2 | |
// | |
Adafruit_MPL115A2 mpl115a2; | |
// | |
// other control | |
// | |
int count = 1; | |
static uint32_t timer; | |
void setup () { | |
Serial.begin(57600); | |
Serial.println("setup started."); | |
// ethernet | |
// | |
if (ether.begin(sizeof Ethernet::buffer, MyMAC) == 0) | |
Serial.println(F("Failed to access Ethernet controller")); | |
if (!ether.dhcpSetup()) | |
Serial.println(F("DHCP failed")); | |
#if USE_DHCP | |
#else | |
EtherCard::copyIp(ether.gwip, GWIP); | |
EtherCard::copyIp(ether.myip, MyIP); | |
#endif | |
#if USE_DNS | |
if (!ether.dnsLookup(hisname)) | |
Serial.println("DNS failed"); | |
#else | |
EtherCard::copyIp(ether.hisip, HisIP); | |
#endif | |
ether.hisport = 2999; | |
ether.printIp("IP: ", ether.myip); | |
ether.printIp("GW: ", ether.gwip); | |
#if USE_DNS | |
ether.printIp("DNS: ", ether.dnsip); | |
#endif | |
ether.printIp("SRV: ", ether.hisip); | |
// | |
// HDC1000 | |
hdc1000.begin(); | |
// | |
// MPL115A2 | |
mpl115a2.begin(); | |
} | |
void loop () { | |
char buf[100], tbuf[10], hbuf[10]; | |
float temperature; | |
float humidity; | |
float pressureHPA = 996; | |
ether.packetLoop(ether.packetReceive()); | |
if (millis() > timer) { | |
timer = millis() + 5000; | |
temperature = hdc1000.getTemp(); | |
dtostrf(temperature, 1, 2, tbuf); | |
humidity = hdc1000.getHumi(); | |
dtostrf(humidity, 1, 2, hbuf); | |
pressureHPA = mpl115a2.getPressure() * 10; | |
Serial.println(); | |
Serial.print("<<< POST "); | |
sprintf(buf, "{ \"temperature\":%s,\"humidity\":%s,\"pressure\":%d }", | |
tbuf, hbuf, (int)pressureHPA); | |
ether.httpPost( | |
PSTR("/weather"), | |
HisName, | |
PSTR("Content-type: application/json"), | |
buf, | |
etherCallback); | |
} | |
} | |
■余談■
今回のハマりポイントは以下の通り- ethercardのetherPostなどを用いる場合は、call backだけでなくloop上にpacketLoopを忘れないこと。普通call backを入れておけばresponseの処理をしてくれる処理系/ライブラリが多いのですが、このライブラリはpacketLoopで実際の送受信処理を行っているようで、入れないと動きません。
- 上記の通り、DHCPを使わない場合でもdhcpSetupを呼び出さないと通信できません。
- 通信相手の指定はあくまでもhisIpとhisport。POSTのパラメータとは関係ありません…って当たり前なんですが、以前使った別のライブラリはGETのパラメータを読み取って自動的に接続してくれていたので、ハマりました。
- 今回サーバ側がわりとシビアにJSON形式を処理するタイプで、フォーマットが違うとokが返ってこないため、POSTの扱いに問題があるのではないかとしばらく悩みました。結果として正しいJSON形式に変更したら、ちゃんとokが返ってきました。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。