ESP8266をUARTアダプタ(?)として使う

海外でのESP8266の使われ方の一つとしてWiFi-UART変換を結構みかけます。これはWiFi側からの入出力をESP8266のUARTの入出力に接続するもので、UARTで制御できる機器をネットワーク越しにtelnetでコントロールできるようにする例が多いようです。もっと簡単に言うと『Teratermなどを使ってWiFi越しにUARTポートを実現する』とでも言えばいいでしょうか。そのうちやってみたいと思うのですが、今回はちょっと調べた例だけ記載したいと思います。

UART変換に関する情報は「ESP8266 WiFi bridge」あたりで検索すると結構引っかかってきます。

検索結果を見てみると、GitHubの「ESP8266-transparent-bridge」というエントリーではWiFi越しにArduinoのプログラミングができた、という記載があります。また、こちらの「MULTIWII (CLEANFLIGHT) WIFI TO EZ-GUI “HOW-TO” – ESP8266」という記事ではMultiWiiというマルチコプター(クアッドコプターはその一種)のコントローラに無線接続するまでが事細かに記載されています。記事の中では、Androidから接続する手順やWindowsから仮想COMポートとして接続する手順まで記載されていますので、参考になるかと思います。

Google Playストアが動かなくなった

Covia F4sでGoogle Playストアへの接続ができなくなってしまいました。「接続できません」という表示と再試行のボタンが表示され、アカウント選択の画面が出るばかりです。戻して「再試行」を押してみても変わりません。半日経っても変わりません。

で、困ったのでぐぐってみてこちらのページを参考に以下のことをやってみたら接続できるようになりましたので、メモしておきます。

「設定」⇒「アプリ」で「すべて」のアプリを表示させて、

  • 7番の「Google Playストアアプリのデータ削除」
  • 「Googleアプリ」のキャッシュを消去(参考にさせていただいたページにはありません)

した後で、端末を再起動させたところ、Google Playストアに接続できるようになりました。どちらが効果があったのかわかりませんが、メモとして残しておきます。

ESP8266でjumpwire.ioを試してみた

ESP8266についてググっていたら、広告欄に何やら「jumpwire.io」なるものが出てました。確か、ESP8266を使ったクラウドサーバーなんちゃらとか(細かい内容は忘れました)。

とりあえずクリックしてみたら「MakerのためのIoTプラットフォーム」「Arduinoを5分でクラウドにつないで、あなたのIoTプロジェクトを成功に導きます。」だそうです。なんでも無料でお試しができるようなので、試してみることにしました。環境はUbuntu14.10LTS 64bit版 + ArduinoIDE1.6.5 + esp8266 version 1.6.5-947-g39819f0(ボードパッケージ)です。ボードパッケージのインストールまでの方法はこちらの記事を参照。

無料サインアップ・・・から先は英語ですが、めげずに登録してみます。よくある「ユーザーIDはメールアドレス」ってやつです。ユーザーIDと設定するパスワードを打ち込むと、メールアドレス宛にアクティベート用のメールが届くので、そこにあるリンクをクリックするとサインアップ完了です。
で、改めてログインし直します。(これもよくあるパターン)

ログインすると、いきなり秘密のトークン(Secret token)が表示されます。この画面をコンソールと呼ぶようです。プロジェクトは無料で5つ作れて、A〜Eというアルファベットになるようです。で、プロジェクトごとにKEYが26個(A〜Z)セットできるようです。

ここで一旦中断し、ローカル側(クライアント側)のプログラミングに戻ります。今回はgithubにライブラリとサンプルがあるようなので、これを動かしてみます。

まず、gitでライブラリを持ってきます。

~$ cd Arduino/libraries/
~/Arduino/libraries$ git clone https://github.com/jumpwire-io/JWIO_ESP8266_ArduinoIDE
Cloning into 'JWIO_ESP8266_ArduinoIDE'...
remote: Counting objects: 35, done.
remote: Total 35 (delta 0), reused 0 (delta 0), pack-reused 35
Unpacking objects: 100% (35/35), done.
Checking connectivity... done.
~/Arduino/libraries$ 

これで~/Arduino/libraries/JWIO_ESP8266_ArduinoIDEの下にサンプルスケッチ(Sample_Sketch.ino)までコピーされますので、これをスケッチが保存されるべき場所にコピーします。

~/Arduino$ mkdir JWIO_ESP8266_ArduinoIDE
~/Arduino$ cd JWIO_ESP8266_ArduinoIDE/
~/Arduino/JWIO_ESP8266_ArduinoIDE$ mkdir Sample_Sketch
~/Arduino/JWIO_ESP8266_ArduinoIDE$ cd Sample_Sketch/
~/Arduino/JWIO_ESP8266_ArduinoIDE/Sample_Sketch$ cp ../../libraries/JWIO_ESP8266_ArduinoIDE/Sample_Sketch.ino .
~/Arduino/JWIO_ESP8266_ArduinoIDE/Sample_Sketch$

これで「スケッチを開く」で、~/Arduino/JWIO_ESP8266_ArduinoIDE/ の下のSample_Sketch.inoを開きます。

サンプルスケッチの冒頭に、

jumpwireIo jwio("your_wifi_ssid","your_wifi_password",
  "your_jwio_secret_token","A");

という箇所がありますので、ここにWiFiの設定と、さっき出たSecret tokenをセットします。4つ目のAは多分プロジェクト名でしょうか、そのままにしておきます。

下の方に

  pinMode(5, OUTPUT);// initialize digital pin 13.

という箇所があります。自分はGPIO2を使うので、ここを5から2に変更しておきます。後の方のdigitalWrite()のところも同様に修正しました。
修正後のソースはこんな感じです。(開示できない箇所はxxxxxxxxxxxxxになってます)

//definitions for jumpwire.io
#include <ESP8266WiFi.h>
#include <JWIO_ESP8266_ArduinoIDE.h>
jumpwireIo jwio("xxxxxxxxxxxxx","xxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","A");


//put your definitions here

//put your global variables here
unsigned long timer;

void setup() {
  jwio.setup(); //always put this code in setup()
  //put your code here
  pinMode(2, OUTPUT);// initialize digital pin 13.
  timer = millis(); // reset timer
}

void loop() {
  jwio.loop(); //always put this code in loop()
  //put your code here
  if (millis() - timer > 5000){
      jwio.Throw('B',millis()/1000.0);
      timer = millis(); // reset timer
    }
}

void Catch(char key, float value){
  //this function is called when byte values on jumpwire.io server are changed
  //put your code here
  if (key == 'A') {
    if(value == 0){          //if value of key A is 0
      digitalWrite(2, LOW);  //turn OFF LED
    }
    else if(value == 1){     //if value of key A is 1
      digitalWrite(2, HIGH); //turn ON LED
    }
  }
}

さて、ここで「マイコンに書き込み」とすると、コンパイルエラーが発生しました。

Sample_Sketch.ino:4:37: fatal error: JWIO_ESP8266_ArduinoIDE.h: No such file or directory

だそうです。そこでサンプルソースの冒頭の

#include <JWIO_ESP8266_ArduinoIDE.h>

#include "JWIO_ESP8266_ArduinoIDE.h"

とすることで、ビルド&書き込み、実行ができました。
ログを見ていると、ところどころでなにやらWebSocket関連でエラーが発生しているっぽい感じもしますが、先に開いたjumpwire.ioのログインコンソール上にKey BとしてESP8266側から経過時間が設定されているのがわかります。(下記画面だと418秒経過後)

Screenshot0001

さらに、Webコンソールの下の方にはData logとしてログが表示されています。

ArduinoIDEのシリアルコンソールにはラウンドトリップタイムも表示されていますが、自分の環境からだと150msくらいかかっているようです。

そして、ブラウザ上で「Value」のところに1をセットして「Throw」を押すと、点灯していたLEDが消灯しました。0をセットして「Throw」を押すと、消灯していたLEDが点灯しました。ソースのコメントとは逆の動作ですが、これは回路の差によるものでしょう。

サンプルソースを修正して以下のようにしてみました。

//definitions for jumpwire.io
#include <ESP8266WiFi.h>
#include "JWIO_ESP8266_ArduinoIDE.h"
jumpwireIo jwio("xxxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","A");


//put your definitions here

//put your global variables here
unsigned long timer;
bool sw=1,osw=1;

void setup() {
  jwio.setup(); //always put this code in setup()
  //put your code here
  pinMode(2, OUTPUT);// initialize digital pin 13.
  timer = millis(); // reset timer
}

void loop() {
  jwio.loop(); //always put this code in loop()
  //put your code here
  if (millis() - timer > 5000){
      jwio.Throw('B',millis()/1000.0);
      timer = millis(); // reset timer
    }
  sw = digitalRead(0);
  if (sw != osw) {
    osw = sw;
    jwio.Throw('C',sw);
  }
}

void Catch(char key, float value){
  //this function is called when byte values on jumpwire.io server are changed
  //put your code here
  if (key == 'A') {
    if(value == 0){          //if value of key A is 0
      digitalWrite(2, LOW);  //turn ON LED
    }
    else if(value == 1){     //if value of key A is 1
      digitalWrite(2, HIGH); //turn OFF LED
    }
  }
}

GPIO0についているスイッチの状態が変化した場合にKey-Cの値をスイッチの状態に応じて0/1に変化させるように修正してみました。(無事動きました)

公式サイトに日本語版の説明もない状態で動かしているため?)ログを見ていると途中でWebsocketが切れるみたいで、安定性に若干不安を感じないでもないですが、jumpwire.ioはシンプルに動くので、確かに手早くクライアント(ESP8266)の状態をブラウザで確認できるようにしておきたい、とにかく短期間でクラウドでデータ収集できるようにしたい、という場合には簡単でいいと思います。

ちなみに、サインアップしてからこの記事を書きつつサンプルを改造したもの(ボタンの状態がアップロードされるように修正したもの)を動かすまでに要した時間は約1.5時間です。5分はちょっと眉唾ものとしても、ArduinoIDEでESP8266が動く状態からなら動かすだけなら30分もあれば十分動かせるでしょう。確かに非常にお手軽ですので、知っておいて損はないと思います。

ESP8266のGPIOをArduinoIDEで扱う

ArduinoIDE上でESP8266モジュールを扱うことができることはわかりました。もちろん、WiFiを使ってクラウド上のサーバーとも通信ができました。

・・・が、ESP8266モジュールの端子をどうやって扱うのか、まだ調べてませんでした。

で、GPIO2に接続したLEDを点滅させてみます。

void setup() {
  pinMode(2, OUTPUT);
}

void loop() {
  digitalWrite(2, LOW);
  delay(1000);
  digitalWrite(2, HIGH);
  delay(2000);
}

これを名前を付けて保存して、GPIO0に付けたスイッチを押したままリセットを解除してブートローダを起動した後、「マイコンボードに書き込む」とするとGPIO2に接続したLEDが点滅しました。

次に、GPIO0の状態を読み取って、GPIO2に出力してみます。

void setup() {
  pinMode(2, OUTPUT);
  pinMode(0, INPUT);
}

void loop() {
  bool v = digitalRead(0);
  digitalWrite(2, v);
  delay(10);
}

同様に書き込んでやると、GPIO0に接続したスイッチのON/OFF状態に応じてLEDが点滅しました。

・・・単純にGPIOの番号を数値で指定してやれば良さそうな感じもしますが、どうなのでしょう。本当はソースまで追って確認したかったのですが、ちょっと短時間では確認しきれませんでした。とりあえず使いたいボタンとLEDだけ確認できたので、残りはボチボチと・・・。

秋月にESP-WROOM-02が登場

ふと、秋月電子のWebサイトをチェックしていたら・・・新製品のところにESP-WROOM-02が載ってました。もちろん技適マーク入り。お値段は1個550円、10個以上だと1個450円。直接買いに行ける(≒送料を考えない)なら、AliExpressで買うよりも早くて安くて安心です。これで、わざわざAliExpressで買う意味がなくなりましたね。

ESP-WROOM-02のデータシート、ESP-8266EXチップセットのデータシートとハードウェアユーザーガイド、技適の証明書(のコピー)も秋月のWebサイトに上がっています。

ESP8266にWebSocketを喋らせてみる

世の中、同じようなことを考える方はいるようで、すでにESP8266+ArduinoIDEでWebSocketライブラリが存在しています。WebSocketが扱えるようになれば、(プログラミングスキルがあれば)クラウド側から好きなタイミングでESP8266に指示をおくることができるようになるはずです。(が、世の中そんなに甘くないんですねぇ〜)

で、今回見つけたWebSocketライブラリは ESP8266-Websocket です。

今回の記事はこれのサンプルを動かしてみよう、と苦戦した記録です。作業PCのOSはUbuntu14.10 LTS 64bit、ArduinoIDEのバージョンは1.6.5です。なお、Ubuntuで動かすにあたっては、

$ sudo addgroup foobar dialout
ユーザー `foobar' をグループ `dialout' に追加しています...
ユーザ foobar をグループ dialout に追加
完了。
$

として、/dev/ttyUSB0のアクセス権をユーザーにつけた後、再ログインしないと/dev/ttyUSB0へアクセスできないとしてエラーになります。

Arduinoのライブラリは ~/Arduino/librariesの下に置けばいいようですので、そこにgitでライブラリを引っ張ってきます。

~$ cd ~/Arduino/libraries
~/Arduino/libraries$ git clone https://github.com/morrissinger/ESP8266-Websocket
Cloning into 'ESP8266-Websocket'...
remote: Counting objects: 286, done.
remote: Total 286 (delta 0), reused 0 (delta 0), pack-reused 286
Receiving objects: 100% (286/286), 105.19 KiB | 0 bytes/s, done.
Resolving deltas: 100% (103/103), done.
Checking connectivity... done.
~/Arduino/libraries$ 

その後、ArduinoIDEで「スケッチ」→「Include Library」→「Manage Libraries」で右上の「Filter your search」で「ESP8266-Websocket」と入力して「INSTALLED」になっていることを確認します。

scrn1

次に、WebSocketClientのデモを動かしてみます。

「ファイル」→「スケッチの例」→「ESP8266-Websocket」→「WebSocketClient_Demo」を選択します。

scren2

ソースの冒頭の

const char* ssid     = "SSID HERE";
const char* password = "PASSWORD HERE";

の部分に自分の無線LANのESSIDとパスキーを入力してから、名前を付けて保存します。

このライブラリはおそらくWindows環境で作成されたのでしょう。Linux環境でビルドしようとすると、ファイル名の大文字小文字で失敗します。そのため、ライブラリの中の以下の部分を修正します。

WebSocketClient.h と WebSocketServer.h の

#include "String.h"

#include "string.h"

に、

WebSocketServer.cpp と WebSocketClient.cppの

#include "base64.h"

#include "Base64.h"

とします。

これでビルドが通るようになりましたが、それでもコネクションに失敗します。禁断のgoto文で接続しに行く部分のみを繰り返すと、何回目か以降は通るようです。ライブラリのWebSocketClient.cppの冒頭にデバッグのための#define文があるので、ここをコメントを外すとデバッグメッセージが出るのですが、これでもハンドシェークの途中で止まっていることはわかるものの、それ以上はなんともわかりません。
これ以上は間にLANアナライザを入れてみてパケット解析しつつ、RFCを読んで何かおかしいのか調べるしかなさそうです。

以下は、現時点でのサンプルプログラムを改造したものです。

#include <ESP8266WiFi.h>
#include <WebSocketClient.h>

const char* ssid     = "xxxxxxxxxxxxxxxx";
const char* password = "xxxxxxxxxxxxxxxx";
char path[] = "/";
char host[] = "echo.websocket.org";
  
WebSocketClient webSocketClient;

// Use WiFiClient class to create TCP connections
WiFiClient client;

void setup() {
  Serial.begin(115200);
  delay(10);

  // We start by connecting to a WiFi network

  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  delay(5000);
  
RETRY:
  // Connect to the websocket server
  IPAddress remote_addr;
  if(!WiFi.hostByName(host,remote_addr)){
    Serial.println("Cannot resolve hostname");
    while(1){}
  } else {
    Serial.println("hostname resolved");
  }
  
  if (client.connect(remote_addr, 80)) {
    Serial.println("Connected");
  } else {
    Serial.println("Connection failed.");
    goto RETRY;
    while(1) {
      // Hang on failure
    }
  }

  // Handshake with the server
  webSocketClient.path = path;
  webSocketClient.host = host;
  if (webSocketClient.handshake(client)) {
    Serial.println("Handshake successful");
  } else {
    Serial.println("Handshake failed.");
    while(1) {
      // Hang on failure
    }  
  }

}


void loop() {
  String data;

  if (client.connected()) {
    
    webSocketClient.getData(data);
    if (data.length() > 0) {
      Serial.print("Received data: ");
      Serial.println(data);
    }
    
    // capture the value of analog 1, send it along
    pinMode(1, INPUT);
    data = String(analogRead(1));
    
    webSocketClient.sendData(data);
    
  } else {
    Serial.println("Client disconnected.");
    while (1) {
      // Hang on disconnect.
    }
  }
  
  // wait to fully let the client disconnect
  delay(3000);
  
}

WebSocketClient.cppをみると、サーバ側からのヘッダの解釈の実装がまだまだであることがわかります。ひとつの可能性として、どうやらここで止まっていそうです。もうひとつは、パケットの受信バッファの処理がどうも変な感じがします。

いずれにせよ、このライブラリはまだそのままでは使えるものではなさそうな感じです。(ま、よくみると色々と制約があるよ、って書いてあるんですけどね)

WebSocketについてちょっと実験してみた

たまたまWebをさまよっていたら、WebSocketの話が目についたので調べてみた。まあ、HTTPに比べると、一旦リンクを張ってしまえばサーバ側からも通信ができるってのが美味しいところ。

で、ブラウザがWebSocketをサポートしているか確認することができるサイトが

http://www.websocket.org/echo.html

なのですが、ここには自分でHTMLを書いて確認したい場合のためのコードが置いてあったりします。で、これを試してみると・・・テキストエディタに貼り付けたら1行になってしまいました(T_T)

整形しなおしたものをここに置いておきます。

<!DOCTYPE html> 
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
  var wsUri = "ws://echo.websocket.org/";
  var output;

  function init() {
    output = document.getElementById("output"); testWebSocket();
  }

  function testWebSocket() {
    websocket = new WebSocket(wsUri);
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onclose = function(evt) { onClose(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
  }

  function onOpen(evt) {
    writeToScreen("CONNECTED");
    doSend("WebSocket rocks");
  }

  function onClose(evt) {
    writeToScreen("DISCONNECTED");
  }

  function onMessage(evt) {
    writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
    websocket.close();
  }

  function onError(evt) {
    writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
  }

  function doSend(message) {
    writeToScreen("SENT: " + message);
    websocket.send(message);
  }

  function writeToScreen(message) {
    var pre = document.createElement("p");
    pre.style.wordWrap = "break-word";
    pre.innerHTML = message;
    output.appendChild(pre);
  }

  window.addEventListener("load", init, false);
</script>
<h2>WebSocket Test</h2>
<div id="output"></div>

これをコピペしたら今度は1行になるようなことはなかったです。

そして、これをローカルで表示させたらこうなりました。

で、ごにょごにょっとしたことを考えているので、自分の理解の範囲で動作を解釈してみます。(間違ってたら指摘いただけると幸いです)

  1. 動作は8行目から始まって、testWebSocket()が呼ばれる。
  2. 13行目のnewでwebsocketのインスタンスを生成。ここでソケットをオープンする。
  3. 14〜17行目はイベントハンドラ。ソケットをオープンすると、この中の14行目のonopenに設定したハンドラが呼ばれる。(先の13行目から呼ばれるところが変な感じがしますが、Javascriptのスコープの特性から14行目で定義している内容を13行目で使用可能なのですね)
  4. 20行目がonopenのイベントハンドラ。この中で、doSend()を呼んでその先でwebsocket.send()でデータを送信している。
  5. websocket.send()の結果、サーバ側からエコーバックが帰ってくる。
  6. エコーバックを受信すると、16行目で定義しているonmessageのハンドラが呼ばれるので、29行目のonMessage()が実行される。この中で、websocket.close()を呼んでコネクションをクローズ。
  7. クローズすると、15行目で定義しているoncloseに対応したハンドラが呼ばれる。

比較的簡単に使えそうですね。
ちょっとだけソケットの使い回しができることを確認してみました。

ソースコードをちょっとだけ弄ります。

<!DOCTYPE html> 
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
  var wsUri = "ws://echo.websocket.org/";
  var output;
 
  var n = 10;

  function init() {
    output = document.getElementById("output"); testWebSocket();
  }
 
  function testWebSocket() {
    websocket = new WebSocket(wsUri);
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onclose = function(evt) { onClose(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
  }
 
  function onOpen(evt) {
    writeToScreen("CONNECTED");
    doSend("WebSocket rocks START");
  }
 
  function onClose(evt) {
    writeToScreen("DISCONNECTED");
  }
 
  function onMessage(evt) {
    writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
    if( n > 0 ){
      doSend("WebSocket rocks " + n);
      n--;
    } else {
      websocket.close();
    }
  }
 
  function onError(evt) {
    writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
  }
 
  function doSend(message) {
    writeToScreen("SENT: " + message);
    websocket.send(message);
  }
 
  function writeToScreen(message) {
    var pre = document.createElement("p");
    pre.style.wordWrap = "break-word";
    pre.innerHTML = message;
    output.appendChild(pre);
  }
 
  window.addEventListener("load", init, false);
</script>
<h2>WebSocket Test</h2>
<div id="output"></div>

変数nに初期値10を設定して、受信時の動作をnの値によって切り替えています。nが0より大きい場合には次のメッセージを送ってnをカウントダウン、nがゼロならソケットをクローズします。
結果は、

Screenshot_soc

となりました。同じソケットを開いたまま11回のメッセージやりとりができていることがわかります。

参考にさせていただいた/参考になりそうなサイト

Covia F4sのその後(とグアムでのプリペイドSIM事情)

Covia F4sを買った話は以前の記事に書きましたが、その後について気づいた点などを書いてみます。

現在はCovia F4sの後継でフラッシュが4GBに、RAMが1MBに増量された(そして色も違う)F4s+が出ていて、実はこっちも持っています。黒が欲しかったのと、某アプリが512MBのRAMではバックグラウンド動作できないためです。
バッテリ容量が小さいのが欠点ですが、手のひらに収まるサイズなのが非常によいところ。F4sの方はF4s+のバッテリ充電器兼いろいろ試してみる対象機になってます。で、お試し機に格下げ(格上げ?)なのでSMS無し版のヨドバシ480円SIMで動かしたりしています。

で、今回、グアムに持っていった(モバイルルータを別途レンタルして使用)のですが、挙動が不審で、何をやってもすぐに再起動。バッテリ充電だけでも再起動。そしてそのうち、反応がなくなって、電池を外して入れなおさざるを得なくなる、という症状が出ました。(F4s+の方は問題なかったのですが・・)

RAM周りのハードウェア異常かと思って、日本に帰国したら修理に出すしかないなぁ、と思っていたら、日本では全く異常がありません。F4sを所有されているかたは要注意かもしれません。

現地SIMがあれば、SIMを入れ替えてどうなるか、とか、SIMを外したらどうなるか、というのも試してみればよかったのかもしれないのですが、日程の関係から今回は現地プリペイドSIMは購入せず。

グアムでのプリペイドSIM事情

ちなみに、グアムではマイクロネシアモール日本語のフロアガイドの2階の真ん中の吹き抜け左側のところ)と、グアムプレミアアウトレットに(店舗一覧。お店はこの地図のMの場所)ドコモパシフィックの店舗があります(今回=2015年7月確認)。どちらも有名なショッピングスポットなので簡単に行けるでしょう。ここでドコモパシフィック(NTTドコモとは別会社です)のSIMを買うことができると思います。(以前、一度アウトレットの方で買ったことがあります)

ドコモパシフィックのプリペイドプランのページはここにあります。ここの記載をみると、プリペイド回線の残高チェック、プリペイド回線間の残高移動、クレジットカードでの残高チャージなどができるようです。また、プリペイドの料金プランはここにあります。SMS、ローカル通話、パケット使い放題3日間で8ドルのようです(が、SIMの価格は入ってないです)。で、10ドル分のプリペイドチャージ付きのSIMが19.95ドルのようです。(追加チャージ用のプリペイドカードは$20、$10、$5の3種類があるようです)

なので、10ドルのチャージ付きのSIMカードを買って、『3日間ローカル通話・ローカルSMS・データ使い放題8ドルコース』か『5日間データ使い放題9ドルだけど、通話とSMSは有料』で使うかのどちらかでしょうね。設定はお店の人にやってもらうのがいいでしょう。

ESP8266からherokuにデータを送ってみる

これまでに node.js + herokuでHTTP GETコマンドを使って最低限のデータの出し入れができることを確認しました。つまりherokuで実用性はないとはいえ、サーバ側の構築ができるようになりました。ただ、セキュリティとか実用性とかそういうのは考えてないので、とにかく最低限ですし、そもそもデータベースを使っていないのでプログラムを再起動されると消えてしまいます。そしてそれは頻繁に起こります。なので、あくまで「ちゃんと作ればできることがわかる」というレベルです。

次は、ESP8266からherokuにデータを送って、PCからそのデータを読み取れるか試してみます。簡単に済ますためにESP8266側は先に確認したようにArduino IDEで構築します。heroku側は先の「hello-heroku2」のままにします。

Arduino側のスケッチはサンプルの「ESP8266WiFi」の下の「WiFiClient」を元にしています。一旦、WiFiClientを開いて、名前(今回は「HerokuClient」にしました)をつけて保存します。
その後、ESSIDとWiFiのパスワードの設定、先の「hello-heroku2」に合わせたGETメソッドに合わせたURLの生成の部分を修正します。今回は、起動すると1から順にデータをheroku側にセットするだけとしました。

修正後のソースは以下のようになりました。元のソースはdata.sparkfun.comのサービスにデータを送るようになっていたようですが、今回はそれを変更しています。(コメントは修正していませんので、そのままになっています。また、WiFiの設定関連は当然伏せてありますので、環境に合わせて修正が必要です。)

/*
 *  This sketch sends data via HTTP GET requests to data.sparkfun.com service.
 *
 *  You need to get streamId and privateKey at data.sparkfun.com and paste them
 *  below. Or just customize this script to talk to other HTTP servers.
 *
 */

#include <ESP8266WiFi.h>

const char* ssid     = "xxxxxxxxxxxxxxxx";
const char* password = "xxxxxxxxxxxxxxxxxxxx";

const char* host = "whispering-basin-7319.herokuapp.com";
int count = 1;

void setup() {
  Serial.begin(115200);
  delay(10);

  // We start by connecting to a WiFi network

  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

int value = 0;

void loop() {
  delay(5000);
  ++value;

  Serial.print("connecting to ");
  Serial.println(host);
  
  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }
  
  // We now create a URI for the request
  String url = "/";
  url += "?DATA=";
  url += String(count++);
  
  Serial.print("Requesting URL: ");
  Serial.println(url);
  
  // This will send the request to the server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" + 
               "Connection: close\r\n\r\n");
  delay(10);
  
  // Read all the lines of the reply from server and print them to Serial
  while(client.available()){
    String line = client.readStringUntil('\r');
    Serial.print(line);
  }
  
  Serial.println();
  Serial.println("closing connection");
}

これで、ESP8266モジュール側をGPIO0をGNDに落とした状態でリセット解除することでブートローダ状態で待機させてから、ArduinoIDEで「マイコンボードに書き込む」とするとコンパイルと書き込みが行われてプログラムが走り出します。
事前にシリアルモニタを起動しておくと、

Screenshot--dev-ttyUSB0

という感じでWiFiのアクセスポイントに接続し、その後herokuへDATAを1から順にセットしている様子が確認できます。(日本語が文字化けしてますが、まあいいでしょう)
PC側からherokuにアクセスすると、

Screenshot-Mozilla Firefox-13

という感じで、ESP8266からセットしたデータを確認することができました。

これでESP8266からherokuへのデータのアップロードと、ブラウザによるherokuにアップロードされたデータの確認ができたことになります。実用にするにはheroku側はデータベース使わないとね、とか、複数のESP8266に対応しないとね、とか、セキュリティ全く無いじゃん、とか、山ほど考慮するべきことがありますが、最低限遊べるレベルには到達したのではないでしょうか。

heroku + node.js + ESP8266 + ArduinoIDEの組み合わせでIoT環境を試すレベルであれば簡単に構築できることがわかりました。もちろん、リソースをかければ本格的な環境にアップグレードすることもできると思います。あとはこの環境をどう使うか、ということですね〜。

Heroku+Node.jsでHelloWorldしてみた(4)

16. クライアントによって異なる動作をさせる

GETメソッドのパラメータが扱えることがわかったので、これで区別させてみたいと思います。まずは適当な作業用のディレクトリを作ります。

~/heroku $ mkdir hello-heroku2
~/heroku $ cd hello-heroku2/

次にソースコードを準備します。内容は下記の通りでhello2.jsという名前で保存します。

var express = require("express");
var app = express();
var hold = undefined; 

app.get('/', function(request, response) {
    console.log(request.query); // for logging
    var data = request.param('DATA');
	if(data){
        hold = data;
	    response.send('データを受け付けました<BR />'
            + '受信したデータは' + hold + 'です。<BR />');
    } else {
        if(hold){
            response.send(
                  '最後に受信したデータは' + hold + 'です。<BR />');
        } else {
            response.send(
                  'まだ受信したデータがありません<BR />');
        }
    }		
})

次に、package.jsonファイルを作成します。

{
  "name": "hello-heroku2",
  "version": "0.0.1",
  "dependencies": {
    "express": "4.13.x"
  },
  "engines": {
    "node": "0.10.x",
    "npm": "1.4.x"
  }
}

これまでの例と名前の部分しか変わっていません。
作成したら、npmで必要なパッケージをインストールします。

~/heroku/hello-heroku2 $ npm install

次にProcfileを用意します。これはファイル名に合わせて修正します。

web: node hello2.js

ローカルでアプリを動かしてみます。

~/heroku/hello-heroku2 $ foreman start
17:37:45 web.1  | started with pid 7322
17:37:45 web.1  | Listening on 5000

ブラウザで「http://localhost:5000/」にまだデータがない旨、表示されます。次に「http://localhost:5000/?DATA=100」としてアクセスした後、再度「http://localhost:5000/」にアクセスすると、セットした値(100)が表示されます。

次にherokuの作成済みのリポジトリにデプロイしてみます。

~/heroku/hello-heroku2 $ git init
Initialized empty Git repository in /home/xxx/heroku/hello-heroku2/.git/
~/heroku/hello-heroku2 $ heroku git:remote -a whispering-basin-7319
set git remote heroku to https://git.heroku.com/whispering-basin-7319.git
~/heroku/hello-heroku2 $ git add .
~/heroku/hello-heroku2 $ git commit -am "1st edtion"
[master (root-commit) be8f876] 1st edtion
 (途中略)
 create mode 100644 package.json
~/heroku/hello-heroku2 $ git push heroku master
Counting objects: 300, done.
 (途中略)
To https://git.heroku.com/whispering-basin-7319.git
 * [new branch]      master -> master
~/heroku/hello-heroku2 $ 

「$ heroku open」として、動作確認してみます。

Screenshot-Mozilla Firefox-10

起動した直後なのでデータがない状態です。
次に、URLを「https://whispering-basin-7319.herokuapp.com/?DATA=12345」としてみます。

Screenshot-Mozilla Firefox-11

データが設定できました。URLを元に戻してリロードし、記憶しているか確認してみます。

Screenshot-Mozilla Firefox-12

データがherokuのサーバ側に記憶されていることが確認できました。