電子工作でインターネットに無線接続して通信を行う (Arduino)
[履歴] [最終更新] (2017/02/12 22:25:50)
1
作品
325
技術情報
最近の投稿
ここは
趣味の電子工作を楽しむ人のための作品販売プラットフォーム

ハードウェア技術を作品にして販売してみませんか?
新着作品

概要

本ページを最初に投稿した 2013/11/10 から 2016/07/24 現在にいたるまでに Arduino のインターネット接続に関する環境がかなり改善されてきましたので、改めて情報をまとめます。

Uploaded Image

四つの選択肢

WiFi シールド

Arduino に接続することで WiFi の機能を拡張できるボードです。公式ページは以下のとおりです。

しかしながら、WiFi シールドは 2016/07/24 現在、日本国内での電波使用に必要な Telec 認定を取得しておらず、不用意に利用すると日本の法律に反してしまいます。日本では代理店である SWITCH SCIENCE社での販売も停止になったようです。

Arduino Yún

数ある Arduino シリーズのうち、Yúnはシールドなしで WiFi の機能を有しているのが特徴です。公式ページはこちらです。当初は Telec 認定を取得していないため日本では WiFi 機能が無効化された状態で販売されていましたが、現在では以下のとおり SWITCH SCIENCE社から技適認証を取得したものが販売されています

メーカーが工事設計認証を取得したため、日本国内でも問題なく無線LANを使う事ができるようになりました。
​販売中です。
(2015/03/09 追記)
http://trac.switch-science.com/wiki/ArduinoYunModified

ただし、価格が他の選択肢と比較すると高めです。

Arduino MKR1000

Yún と同様に WiFi の機能を内蔵した Arduino です。公式ページはこちらです。しかしながら 2016/07/24 現在、日本での販売は開始されていません。

WiFi モジュール

Arduino とは関係のない汎用の WiFi 接続用 IC を利用する方法です。

XBee

WiFi (Wi-Fi, Wifiとも。IEEE 802.11シリーズの総称のようなもの) と同様に、1対1の無線通信を行うための規格に ZigBee (IEEE 802.15) があります。XBee は WiFi ではなく当初 IEEE 802.15.4 に基づいて通信を行うように設計された IC のブランド名です。後に WiFi を用いて通信するタイプの XBee (XBee WiFi) も登場しました。XBee シリーズは Digi International 社によって Arduino とは全く関係なく設計されたものですのでこのままでは Arduino のシールドとして使えません。そこで、Wireless Proto シールドという、Arduino のための XBee アダプタシールドが用意されました。公式ページはこちらです。XBeeWiFi は日本国内で適法に使用できる「認証」がある製品ですので、安心して使用できます。SWITCH SIENCE 社から 4,000 円程度で購入できます。

ESP-WROOM-02 (ESP8266 を内蔵)

ESP8266 は 32 bit Tensilica MCU (マイコン; MicroController Unit) を内蔵した WiFi モジュールです。更に ESP8266 を内蔵して作られたのが ESP-WROOM-02 です。ESP-WROOM-02 は日本での技適も取得済みのため安心して使用できます。SWITCH SIENCE 社から 900 円程度で購入できます。通常の電子工作では以下のピッチ変換されたものを利用します。Amazon でもやはり SWITCH SCIENCE 社から販売されています

本ページでは Arduino をインターネット接続することを考えていますので対象外ですが、ESP8266 自体がマイコンを内蔵しているため、Arduino なしで単体でも Arduino 互換機として電子工作に利用できます。そのための商品も販売されています。

WiFi モジュール ESP-WROOM-02 と Arduino の接続方法

日本国内で安心して使用でき、情報も充実しており、価格も安い ESP-WROOM-02 を使用することにします。

参考資料

回路図

Arduino シリーズの中でも安価な UNO を利用することにします。2016/07/24 現在の最新版は R3 (Rev3) です。以下のように回路を組みます。ESP-WROOM-02 と Arduino UNO の動作電圧が 3.3v および 5v と異なるため、厳密に作るためには電圧レベルをシフトする必要がありますが、今回のような簡単な回路ではあまり気にせずにそのまま接続しても動きます。シリアル通信の方式の一つである I2C に関するページからも該当箇所を引用します。

Connecting the 5V Arduino directly to a single 3.3V-powered I2C chip usually works, even though it violates official specifications in multiple ways.
http://playground.arduino.cc/Main/I2CBi-directionalLevelShifter

ESP-WROOM-02 and Arduino UNO Rev3
Uploaded Image

SoftwareSerial を使用

回路図に記載のとおり、以下の構成で接続します。

ESP-WROOM-02/TXD <-> Arduino/2番ピン (SoftwareSerial で RX に設定)
ESP-WROOM-02/RXD <-> Arduino/3番ピン (SoftwareSerial で TX に設定)

公式ページに記載されているように、Arduino には HardwareSerial の 0 番ピン (RX) と 1 番ピン (TX) が備わっています。この HardwareSerial を利用して以下のように接続しても ESP-WROOM-02 と通信できます。

ESP-WROOM-02/TXD <-> Arduino/0番ピン (Hardwareserial RX)
ESP-WROOM-02/RXD <-> Arduino/1番ピン (Hardwareserial TX)

しかしながら、この HardwareSerial は Arduino IDE から Arduino に USB ケーブルを介してシリアル通信でプログラムを書き込むときにも使用されるため、以下の書き込みエラーが発生する原因になります。0 番ピンと 1 番ピンから ESP-WROOM-02 への接続線を外せば書き込みに成功するようになります。

avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x68
avrdude: stk500_getsync() attempt 2 of 10: not in sync: resp=0x69
avrdude: stk500_getsync() attempt 3 of 10: not in sync: resp=0x2d
avrdude: stk500_getsync() attempt 4 of 10: not in sync: resp=0x70
avrdude: stk500_getsync() attempt 5 of 10: not in sync: resp=0x72
avrdude: stk500_getsync() attempt 6 of 10: not in sync: resp=0x65
avrdude: stk500_getsync() attempt 7 of 10: not in sync: resp=0x0d
avrdude: stk500_getsync() attempt 8 of 10: not in sync: resp=0x0a
avrdude: stk500_getsync() attempt 9 of 10: not in sync: resp=0x68
avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x69

開発中に毎回ピンから外すのは手間になるため、以下では SoftwareSerial を使用します。ESP-WROOM-02 の出荷時の設定ではシリアル通信の baudrate が 115200 bps に設定されていますが、SoftwareSerial を UNO で使用した場合は 115200 bps まで設定できるため問題ありません。

HTTP 通信のサンプルコード

ITEADLIB_Arduino_WeeESP8266 は TCP/IP 通信を行う汎用のライブラリですが、シリアル通信用のバッファサイズの既定値が 64 bytes となっている Arduino UNO などについて、そのままでは利用できません。そこで、上述の参考資料をもとに HTTP クライアント用のクラスをライブラリ化してみます。今回は IoT 電子工作に最低限必要な機能を提供するシンプルな設計を目標とします。目的に応じてライブラリの設計を変えてみてください。UNO をサポートしない、SoftwareSerial をサポートしない、電圧レベルシフタの使用を許容する、等によって制約を緩めれば送信だけでなく受信もできるライブラリが作成できます。HTTP の基本的な知識についてはこちらをご参照ください。

  • シリアルバッファが 64 バイトの Arduino UNO でも使用可能 (ESP-WROOM-02 経由)
  • HTTP GET と POST に対応
    • データ受信ではなく「送信」を目的とします。
    • メモリが少ない UNO などが存在することを考えると HTTP レスポンスをすべて処理することは現実的ではありません。
    • SoftwareSerial にはパリティビットの付与設定begin で指定できないため、電圧レベルシフタを用意しない今回の回路では、3.3v デバイスの ESP-WROOM-02 からデータを受信するときにデータが壊れやすくなります。
    • レスポンスボディを取得する関数は提供せず、ステータスコード取得関数のみ提供します。
  • SSL/TLS 通信には非対応

ライブラリのインストール方法について

今回サンプルとして作ったライブラリzip 形式で github からダウンロードできます。Arduino IDE のメニューから追加してご使用ください。ライブラリ追加方法に関する公式ページはこちらです。

WiFi アクセスポイントに接続するだけのサンプル

ConnectWiFi.ino

#include <HttpClient_ESP8266_AT.h>

// Arduino UNO の場合は、例えばデジタル入出力の 2 番, 3 番ピンを利用して
// ESP-WROOM-02 とシリアル通信するように設定します。
const byte rxPin = 2; // Wire this to Tx Pin of ESP8266
const byte txPin = 3; // Wire this to Rx Pin of ESP8266
HttpClient_ESP8266_AT httpClient(rxPin, txPin);

void setup() {
    // PC と通信する HardwareSerial の baudrate を設定します。
    // ESP-WROOM-02 の baudrate とは関係ありません。
     Serial.begin(9600);

    // ESP-WROOM-02 との接続確認を行います。
    while(true) {
        if(httpClient.statusAT()) {
            Serial.println("AT status OK");
            break;
        }
        else {
            Serial.println("AT status NOT OK");
        }
        delay(1000);
    }

    // SSID と PASSWORD でアクセスポイントに接続します。
    while(true) {
        if(httpClient.connectAP("SSID", "PASSWORD")) { // <- 書き換えてください。
            Serial.println("Successfully connected to an AP");
            break;
        }
        else {
            Serial.println("Failed to connected to an AP. retrying...");
        }
        delay(1000);
    }
}

void loop() {
    // 本サンプルでは特に通信はせず、WiFi の接続が切れていないかどうかを
    // 1 秒毎に確認して結果を出力しつづけます。
    if(httpClient.statusWiFi()) {
        Serial.println("WiFi status OK");
    }
    else {
        Serial.println("WiFi status NOT OK");
    }
    delay(1000);
}

HTTP GET リクエストのサンプル

HTTPGET.ino

#include <HttpClient_ESP8266_AT.h>

// Arduino UNO の場合は、例えばデジタル入出力の 2 番, 3 番ピンを利用して
// ESP-WROOM-02 とシリアル通信するように設定します。
const byte rxPin = 2; // Wire this to Tx Pin of ESP8266
const byte txPin = 3; // Wire this to Rx Pin of ESP8266
HttpClient_ESP8266_AT httpClient(rxPin, txPin);

void setup() {
    // PC と通信する HardwareSerial の baudrate を設定します。
    // ESP-WROOM-02 の baudrate とは関係ありません。
    Serial.begin(9600);

    // SSID と PASSWORD でアクセスポイントに接続します。
    // シリアル接続確認と WiFi 接続確認を行い、設定不備があれば出力します。
    while(true) {
        if(httpClient.statusAT()) { Serial.println("AT status OK"); break; }
        else Serial.println("AT status NOT OK");
        delay(1000);
    }
    while(true) { // 書き換えてください↓
        if(httpClient.connectAP("SSID", "PASSWORD")) { Serial.println("Successfully connected to an AP"); break; }
        else Serial.println("Failed to connected to an AP. retrying...");
        delay(1000);
    }
    while(true) {
        if(httpClient.statusWiFi()) { Serial.println("WiFi status OK"); break; }
        else Serial.println("WiFi status NOT OK");
        delay(1000);
    }
}

void loop() {
    // ノイズの影響でリクエストに失敗することがあるため while ループで囲います。
    while(true) {
        // HTTP GET リクエストを実行します。
        httpClient.get("www.example.com", "/");

        // レスポンス status code が 0 以上であればリクエスト成功です。
        //   -1: ノイズ等でシリアル通信ができず、そもそもデータ送信ができなかった。
        //    0: データ送信はできたが、受信データがノイズで壊れてしまっていた。
        //   else (>0): 受信データから正常に HTTP レスポンス status code が得られた。

        if(httpClient.responseStatusCode() >= 0) {
            // 本ページでは電圧レベルシフタを用意していない想定のため、
            // 受信データが壊れている可能性が高く、したがって 200 が 100 になっていたりするため、
            // 値には興味をもたず、0 以上の値が得られることが重要であると考えてコーディングします。
            Serial.println("SUCCESS");
            break; // 成功したので while を抜けます。
        }
        else {
            Serial.println("FAILURE, retrying...");
        }
    }

    // 本サンプルでは 1 秒毎に GET を繰り返します。
    delay(1000);
}

HTTP POST リクエストのサンプル

HTTPPOST.ino

#include <HttpClient_ESP8266_AT.h>

// Arduino UNO の場合は、例えばデジタル入出力の 2 番, 3 番ピンを利用して
// ESP-WROOM-02 とシリアル通信するように設定します。
const byte rxPin = 2; // Wire this to Tx Pin of ESP8266
const byte txPin = 3; // Wire this to Rx Pin of ESP8266
HttpClient_ESP8266_AT httpClient(rxPin, txPin);

void setup() {
    // PC と通信する HardwareSerial の baudrate を設定します。
    // ESP-WROOM-02 の baudrate とは関係ありません。
    Serial.begin(9600);

    // SSID と PASSWORD でアクセスポイントに接続します。
    // シリアル接続確認と WiFi 接続確認を行い、設定不備があれば出力します。
    while(true) {
        if(httpClient.statusAT()) { Serial.println("AT status OK"); break; }
        else Serial.println("AT status NOT OK");
        delay(1000);
    }
    while(true) { // 書き換えてください↓
        if(httpClient.connectAP("SSID", "PASSWORD")) { Serial.println("Successfully connected to an AP"); break; }
        else Serial.println("Failed to connected to an AP. retrying...");
        delay(1000);
    }
    while(true) {
        if(httpClient.statusWiFi()) { Serial.println("WiFi status OK"); break; }
        else Serial.println("WiFi status NOT OK");
        delay(1000);
    }
}

void loop() {
    // ノイズの影響でリクエストに失敗することがあるため while ループで囲います。
    while(true) {
        // HTTP POST リクエストを実行します。
        httpClient.post("www.example.com", "/", "name=hoge%27hoge&val=123", "application/x-www-form-urlencoded");

        // レスポンス status code が 0 以上であればリクエスト成功です。
        //   -1: ノイズ等でシリアル通信ができず、そもそもデータ送信ができなかった。
        //    0: データ送信はできたが、受信データがノイズで壊れてしまっていた。
        //   else (>0): 受信データから正常に HTTP レスポンス status code が得られた。

        if(httpClient.responseStatusCode() >= 0) {
            // 本ページでは電圧レベルシフタを用意していない想定のため、
            // 受信データが壊れている可能性が高く、したがって 200 が 100 になっていたりするため、
            // 値には興味をもたず、0 以上の値が得られることが重要であると考えてコーディングします。
            Serial.println("SUCCESS");
            break; // 成功したので while を抜けます。
        }
        else {
            Serial.println("FAILURE, retrying...");
        }
    }

    // 本サンプルでは 1 秒毎に POST を繰り返します。
    delay(1000);
}

IFTTT と連携して IoT 電子工作

IFTTT (If This, Then That) は、もしも (if) ある入力 (this) があったならば、別のある出力処理 (that) を実行する多目的な Web サービスです。様々な入力方式 (if this) が提供されており、特に Maker を入力 (this) に指定して、Arduino から HTTP GET または POST でデータを送信することで IoT 電子工作が実現できます。出力方式 (that) には Gmail, Slack, Twitter, スマホ通知 など様々なものが提供されています。ここでは特に Gmail を出力にして HTTP POST でメール送信してみます。

Uploaded Image

JSON データを POST の body で指定することで、メールタイトルや本文に値を埋め込むことができます。レシピを作成するときに認証許可したアカウントの gmail から、レシピ作成時にカンマ区切りで指定した複数の宛先にメールが送信されます。Arduino の設定に必要な認証情報は以下の二つです。Key はこちらのページで確認できる値です。

Event Name: arduino_esp8266_gmail
Key: sw8UBoTxxKAykBbG7y_8H

gmail.ino

#include <HttpClient_ESP8266_AT.h>

// Arduino UNO の場合は、例えばデジタル入出力の 2 番, 3 番ピンを利用して
// ESP-WROOM-02 とシリアル通信するように設定します。
const byte rxPin = 2; // Wire this to Tx Pin of ESP8266
const byte txPin = 3; // Wire this to Rx Pin of ESP8266
HttpClient_ESP8266_AT httpClient(rxPin, txPin);

void setup() {
    // PC と通信する HardwareSerial の baudrate を設定します。
    // ESP-WROOM-02 の baudrate とは関係ありません。
    Serial.begin(9600);

    // SSID と PASSWORD でアクセスポイントに接続します。
    // シリアル接続確認と WiFi 接続確認を行い、設定不備があれば出力します。
    while(true) {
        if(httpClient.statusAT()) { Serial.println("AT status OK"); break; }
        else Serial.println("AT status NOT OK");
        delay(1000);
    }
    while(true) { // 書き換えてください↓
        if(httpClient.connectAP("SSID", "PASSWORD")) { Serial.println("Successfully connected to an AP"); break; }
        else Serial.println("Failed to connected to an AP. retrying...");
        delay(1000);
    }
    while(true) {
        if(httpClient.statusWiFi()) { Serial.println("WiFi status OK"); break; }
        else Serial.println("WiFi status NOT OK");
        delay(1000);
    }
}

void loop() {
    // ノイズの影響でリクエストに失敗することがあるため while ループで囲います。
    while(true) {

        // IFTTT への HTTP POST リクエストを実行します。SSL/TLS 通信である必要はなく 80 番ポートを利用できます。
        // 以下の二つの項目は、お使いのアカウントで表示されているものに書き換えてください。
        // - Event Name: arduino_esp8266_gmail
        // - Key: sw8UBoTxxKAykBbG7y_8H
        httpClient.post("maker.ifttt.com", // IFTTT のホスト名
                        "/trigger/arduino_esp8266_gmail/with/key/sw8UBoTxxKAykBbG7y_8H", // <- 書き換えてください。
                        "{\"value1\":\"ABC\",\"value2\":\"123\",\"value3\":\"456\"}", // JSON データ
                        "application/json");

        // レスポンス status code が 0 以上であればリクエスト成功です。
        //   -1: ノイズ等でシリアル通信ができず、そもそもデータ送信ができなかった。
        //    0: データ送信はできたが、受信データがノイズで壊れてしまっていた。
        //   else (>0): 受信データから正常に HTTP レスポンス status code が得られた。

        if(httpClient.responseStatusCode() >= 0) {
            // 本ページでは電圧レベルシフタを用意していない想定のため、
            // 受信データが壊れている可能性が高く、したがって 200 が 100 になっていたりするため、
            // 値には興味をもたず、0 以上の値が得られることが重要であると考えてコーディングします。
            Serial.println("SUCCESS");
            break; // 成功したので while を抜けます。
        }
        else {
            Serial.println("FAILURE, retrying...");
        }
    }

    // 本サンプルでは特に他の処理はしません。
    while(true); // ずっと待機
}

送信される予定のメール

Uploaded Image

タイトル

The event named "arduino_esp8266_gmail" occurred on the Maker Channel

本文の例

What: arduino_esp8266_gmail
When: August 4, 2016 at 01:10AM
Extra Data: ABC, 123, 456, 

Qoosky Cloud Controller と IFTTT を同時に使ってみる

Qoosky Cloud Controller はインターネット接続されたデバイスをスマートフォンや PC から操作するための BaaS API です。Arduino に関するチュートリアルは『Qoosky Cloud Controller in 5 Minutes』です。専用の SDK を用いてライブラリをインストールします。

先程の IFTTT と併用することで、スマートフォンや PC から入力 (if this) を制御できるようになります。UNO を使用する場合は特に不足しがちな SRAM を節約するために F() マクロで文字列を囲んでいます。

gmail_controller.ino

#include <Qoosky_ESP8266_AT.h>
#include <HttpClient_ESP8266_AT.h>

// Arduino UNO の場合は、例えばデジタル入出力の 2 番, 3 番ピンを利用して
// ESP-WROOM-02 とシリアル通信するように設定します。
const byte rxPin = 2; // Wire this to Tx Pin of ESP8266
const byte txPin = 3; // Wire this to Rx Pin of ESP8266
SoftwareSerial softwareSerial(rxPin, txPin);

Qoosky_ESP8266_AT qoosky(softwareSerial); // 同じ SoftwareSerial を共有
HttpClient_ESP8266_AT httpClient(softwareSerial); // 同じ SoftwareSerial を共有

void setup() {
    Serial.begin(9600); // PC との通信で使用する baudrate
    softwareSerial.begin(115200); // ESP-WROOM-02 との baudrate

    while(true) {
        if(qoosky.statusAT()) { Serial.println(F("AT status OK")); break; }
        else Serial.println(F("AT status NOT OK"));
        delay(1000);
    }
    while(true) { // 書き換えてください↓
        if(qoosky.connectAP(F("SSID"), F("PASSWORD"))) { Serial.println(F("Successfully connected to an AP")); break; }
        else Serial.println(F("Failed to connected to an AP. retrying..."));
        delay(1000);
    }
    while(true) {
        if(qoosky.statusWiFi()) { Serial.println(F("WiFi status OK")); break; }
        else Serial.println(F("WiFi status NOT OK"));
        delay(1000);
    }
    while(true) { // 書き換えてください↓
        if(qoosky.connectQoosky(F("XXXX-XXXX-XXXX-XXXX"))) { Serial.println(F("Successfully connected to Qoosky.")); break; }
        else Serial.println(F("Failed to connect to Qoosky, retrying..."));
        delay(1000);
    }
}

void loop() {
    int key = qoosky.popPushedKey();
    if(key) {
        Serial.println("Received: " + String(key) + " (send gmail if key is '8')");
        if(key == 8) { // コントローラの 8 番ボタンが押されたら gmail を送信します。
            while(true) {
                // WebSocket 通信は一旦切断されます。90 秒以内に再接続されます。
                httpClient.post(F("maker.ifttt.com"), // 書き換えてください↓
                                F("/trigger/arduino_esp8266_gmail/with/key/ck8xvpYdBOUeFj2nfZY9yE"),
                                "{\"value1\":\"" + String(key) + "\",\"value2\":\"" + String(key) + "\",\"value3\":\"" + String(key) + "\"}",
                                F("application/json"));
                if(httpClient.responseStatusCode() >= 0) {
                    Serial.println(F("send gmail SUCCESS"));
                    break; // 成功したので while を抜けます。
                }
                else Serial.println(F("send gmail FAILURE, retrying..."));
            }
            // 本サンプルでは特に他の処理はしません。
            while(true); // ずっと待機
        }
    }
    else Serial.println(F("No pushed key."));
    delay(100);
}