Maker活動

2015年8月16日日曜日

合計1000円以下のArduino+Etherからサーバへデータを送る:Arduino側



今回は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ピン
VCC3.3v電源
GNDGND
CS8
RSTReset(省略可)
SI11
SCK13
SO12

別の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用ライブラリは下記の通りです。

モジュールライブラリ
HDC1000https://github.com/ftruzzi/HDC1000-Arduino
MPL115A2https://github.com/adafruit/Adafruit_MPL115A2
ENC28J60https://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です。

#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 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。