こんにちは、イシイです。
Arduinoからネットワーク接続を行おうとする場合、イーサネットケーブル有りであればイーサネットシールド、3Gであれば3Gシールド3GIMなどがありますが、ケーブルが煩わしかったり値段がお高かったりします。WiFi シールドであればその辺りは比較的改善できそうではありますが、今回はもっと格安で小さくてネットワーク接続できるWi-Fiモジュール「ESP8266」が搭載されているESP-WROOM-02を使ってみました。

ESP8266

ESP8266は、技適も通っているWi-Fiモジュールなので、日本での使用も特に問題ありません。また、ESP8266自体にもArduinoスケッチを書き込めるようなので、モジュール単体だけでセンサーなどの扱いとネットワーク接続の両方ができる、なかなか万能な子です。

今回使用するモジュールについて

ESP-WROOM-02は、値段も安くサイズも小さいのですが、それ単体だとESP8266とシリアル通信を行うためのコネクタや、電圧変換を行う仕組みなどの部分は自分で作らなければなりません。今回は、その辺りをまるっと解消してくれている「ESP-WROOM-02開発ボード」を使うことにしました。

準備

準備作業としてはピンヘッダをはんだ付けするだけです。5Vを3.3Vに出力してくれるレギュレータ機能も持っているので、PCとのシリアル通信には搭載されているコネクタにマイクロUSBを接続するだけで、すぐに利用ができます。完全にこのボードひとつだけで完結してくれます。

シリアル通信の確認

ピンヘッダをはんだ付けしたESP-WROOM-02をブレッドボードに挿し、マイクロUSBを使ってMacと接続します。この状態で給電もできているので、シリアルモニターからシリアル通信を行ってみます。 Arduino > Tools > Port から、接続しているUSB(/dev/cu.usbserial-xxxxxx)を選択します。シリアルモニターを開いて、速度を115200bpsにし、改行コードを「Both NL & CR」に変更します。 ESP-WROOM-02のリセットボタンを押すと文字化けデータの後に「ready」と表示されればシリアル通信ができているので、コマンド入力欄に「AT」と入力して送信すると「OK」と返してきます。これでATコマンドのやりとりも確認できました。

もう少しATコマンドを叩いてみる

さらに、いくつかのATコマンドを試してみます。

MACアドレスの確認コマンド

AT+CIPSTAMAC?
+CIPSTAMAC:"xx:xx:xx:xx:xx:xx"
OK

MACアドレスなどでネットワーク制限がかかっている場合には、こちらでMACアドレスが取得可能です。

Station mode

AT+CWMODE=1 (Station Mode)
AT+CWMODE=2 (SoftAP Mode)
AT+CWMODE=3 (Station&SoftAP Mode)

パラメータによって、モードを変換できるようです。今回は

AT+CWMODE=3
OK

としてみました。

SSIDとパスワードを入力してAPに接続

AT+CWJAP="SSID","PassWord"

でWi-Fiに接続可能です。接続を試してみたところ、

AT+CWJAP="xxxxx","xxxxx"
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IP
OK

となりました。

IPアドレスやアクセスポイントのMACアドレスの取得

AT+CIFSR
+CIFSR:APIP,"xxx.xxx.xxx.xxx"
+CIFSR:APMAC,"xx:xx:xx:xx:xx:xx"
+CIFSR:STAIP,"xxx.xxx.xxx.xxx"
+CIFSR:STAMAC,"xx:xx:xx:xx:xx:xx"
OK

IPアドレスやMACアドレスはこれで確認できます。先程、ステーションモードを3にしたので、子機モードとソフトAPモードの2種類の情報が返されてきました。

通信速度を変える

AT+UART_DEF=9600,8,1,0,0
OK

Arduinoとのシリアル通信を9600bpsで行うので、これで変更しておきます。

今回作ってみる仕組み

前述の通り、ESP8266自体にArduinoスケッチを書き込めるので、このESP-WROOM-02だけでも完結できるのですが、今回はArduinoからESP-WROOM-02を単純にWi-Fiモジュールとしてだけ使うような形にしてみることにしてみます。タクトスイッチを押すとURLにアクセスし、戻ってきたJSONの値を解析するような構成にしてみました。

配線

Arduino側

Arduino 接続先 備考
D13ピン ESP-WROOM-02 RX ソフトウェアシリアル通信用
D12ピン ESP-WROOM-02 TX ソフトウェアシリアル通信用
D3ピン タクトスイッチ スイッチON/OFF監視
5Vピン ブレッドボード + ESP-WROOM-02電源供給
GNDピン ブレッドボード -
GNDピン ブレッドボード -

ESP-WROOM-02側

ESP-WROOM-02 接続先 備考
VIN ブレッドボード + Arduinoからの電源供給
GND ブレッドボード -
5V ブレッドボード + タクトスイッチ電源供給

配線図

circuit.jpg

スケッチ

スケッチは、Arduino側に書き込みますので、ボード選択もArduinoにしておきます。setup関数内でWi-Fiのコネクションを張り、タクトスイッチが押されたら指定のURLにアクセスし、JSONのresultを判別するような仕組みにしています。JSONの解析には、ArduinoJsonというライブラリを使用しました。

#include <SoftwareSerial.h>
#include <ArduinoJson.h> //Json Parser Library

#define SSID "xxxxxxxxxx"
#define PASSWORD "xxxxxxxxxx"

#define HOST_URL "xxxxxxxxxx" //Don't include space
#define ACCESS_DIR "xxxxxxxxxx" //Program Directory
#define HOST_PORT 80
#define USER_AGENT "ESP8266" //Set Free

#define TO_TX_PIN 12 //Arduino 13pin to ESP8266 TX
#define TO_RX_PIN 13 //Arduino 12pin to ESP8266 RX
#define SERIAL_WAIT_TIME 5000

#define SWITCH_PIN 3
int buttonState = 0;

SoftwareSerial esp8266Serial(TO_TX_PIN, TO_RX_PIN);

void setup() {

  //Serial Setting
  Serial.begin(9600);
  esp8266Serial.begin(9600);
  esp8266Serial.setTimeout(SERIAL_WAIT_TIME);
  
  Serial.println("--Setup Start--");

  //WiFi Setting
  bool setupFinish = false;
  do {

    Serial.println("Try Connect WiFi");
    setupFinish = setupWiFi();
    if (setupFinish) {
      Serial.println("Connected");
    } else {
      Serial.println("Not Connected");
    }
    
  } while(setupFinish == 0);

  //Switch Setting
  pinMode(SWITCH_PIN, INPUT);

  Serial.println("--Setup End--");

}

void loop() {

  //Trigger Button
  buttonState = digitalRead(SWITCH_PIN);

  if (buttonState == HIGH) {
    
    if (sendRequest()) {
      sendATCommand("AT+CIPCLOSE", 0);
    } else {
      Serial.println("Request Failed");
    }

  }

}

bool setupWiFi() {

  //AT+RST > Reset
  //AT+CWJAP="SSID","PASSWORD" > WiFi-Connect
  if(sendATCommand("AT+RST", SERIAL_WAIT_TIME)) {
    String wifiConnectCmd = "AT+CWJAP=\"";
    wifiConnectCmd += SSID;
    wifiConnectCmd += "\",\"";
    wifiConnectCmd += PASSWORD;
    wifiConnectCmd += "\"";
    return sendATCommand(wifiConnectCmd, SERIAL_WAIT_TIME);
  } else {
    return false;
  }

}

bool sendATCommand(String cmd, int waitTime) {

  //Serial Command Send
  esp8266Serial.print(cmd);
  esp8266Serial.print("\r\n");
  
  //Command Log
  Serial.print("AT Command > ");
  Serial.println(cmd);

  delay(waitTime);

  //Response Check From Serial Buffer
  if(esp8266Serial.find("OK")) {
    Serial.println("OK");
    return true;
  } else {
    Serial.println("NG");
    return false;
  }
  
}

bool sendRequest() {

  //Establish TCP Connection
  //AT Command > AT+CIPSTART="TCP","HOST_NAME",HOST_PORT
  String tcpConnectCmd = "AT+CIPSTART=\"TCP\",\"";
  tcpConnectCmd += HOST_URL;
  tcpConnectCmd += "\",";
  tcpConnectCmd += HOST_PORT; 
  sendATCommand(tcpConnectCmd, SERIAL_WAIT_TIME);

  //Header Info
  String requestHeader = "GET ";
  requestHeader += ACCESS_DIR;
  requestHeader += " HTTP/1.1\r\n";
  requestHeader += "Host: ";
  requestHeader += HOST_URL;
  requestHeader += ":";
  requestHeader += HOST_PORT;
  requestHeader += " User-Agent: ";
  requestHeader += USER_AGENT;
  requestHeader += " Connection: close ";
  requestHeader += "\r\n\r\n";  
  Serial.println(requestHeader);

  //Send Data to Host
  //AT Command > AT+CIPSEND=header.length
  String sendDataCmd = "AT+CIPSEND=";
  sendDataCmd += requestHeader.length();
  sendATCommand(sendDataCmd, SERIAL_WAIT_TIME);

  //Start Transmission
  if(esp8266Serial.find(">")) {

    //Access to Host
    esp8266Serial.print(requestHeader);
    delay(500);

    //Data Receive
    if(esp8266Serial.available()) {
      
      //Get Content Data
      String responseData;

      //Response Header Trim
      //Until the blank line is the response header
      while(esp8266Serial.available()){
        responseData = esp8266Serial.readStringUntil('\r');
        responseData.trim();
        if (responseData.length() == 0) {
          break;
        } else {
          Serial.println(responseData);
        }
      }
      
      //Response Body = JSON Data Get
      while(esp8266Serial.available())
      {
        responseData = esp8266Serial.readStringUntil('\r');
        responseData.trim();
        Serial.println(responseData);
      }

      //JSON Data Parse
      const int BUFFER_SIZE = JSON_OBJECT_SIZE(4) + JSON_ARRAY_SIZE(1);
      StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;

      char json[responseData.length() + 1];
      responseData.toCharArray(json, sizeof(json));
      JsonObject& root = jsonBuffer.parseObject(json);

      if (!root.success()) {
        Serial.println("Parse failed");
        return false;
      }

      //{"result":"OK"}
      const char* result = root["result"];
      Serial.println(result);
      Serial.println();

      if (strcmp(result, "OK") == 0) {
        Serial.println("Result OK");
        return true;
      
      } else {
        Serial.println("Result NG");
        return false;
      }
      
    } else {
      Serial.println("Data Not Received");
      return false;
    }
    
  } else {
    Serial.println("Transmission Failed");
    return false;
  }
  
}

感想

ESP8266は、Arduinoスケッチも書き込めて値段も安いということもあり、とても扱いやすいモジュールなのですが、どうやらデフォルトバージョンではATコマンドがSSL通信には対応していないようで、そこだけ惜しいところです。とはいえ、SSL通信を使わない、または他の代替手段で対応してしまうのであれば、現時点では非常に有用性の高いモジュールと言えそうです。