読者です 読者をやめる 読者になる 読者になる

kur.jp

バイオリンと自転車をこよなく愛するkurのチラシの裏。たまには技術的なことを書いたりするかも知れません。

格安WIFIモジュールESP8266をArduino Unoで使う

Arduino

Arduinoで何かを作ろうとする時にデータをインターネットから取得したりだとか、またはインターネットに対して送信したりだとか、そういうことがしたくなる時がある。

こういった時に選択肢はいくつかある。

  1. Arduino用のWiFIシールドを用意する
  2. Arduino YUN等の、WiFI標準搭載のArduinoを使用する
  3. シリアル接続のWiFIモジュールを使用する。

1に関しては、下記のページにあるように純正品が存在するのだけれど、Amazonやスイッチサイエンスでも2016年6月10日現在は取り扱っていないようだし、少なくとも日本国内では入手困難にであるように思われます。

Arduino - ArduinoWiFiShield

Arduion YUNは日本でも容易に入手可能ではあるものの1万円程度と、結構な値段がしてしまう。

Arduino YÚN

Arduino YÚN


そこで、有力な選択肢となるのが、ESP8266である。Amazonだと一個500円程度かな。

もし急がないようであれば、Aliexpressで送料込で1個2ドル弱からある。

www.aliexpress.com

この値段でArduinoをWiFi対応に出来るのであるから、非常にコストパフォーマンスが高いと言えるのだが、実際に使えるようになるまで色々と詰まってしまったので、備忘録代わりにここに手順を書き残して置く。

1. ライブラリのダウンロードとインストールし、コードの修正

github.com

上記からArduinoからESP8266を使用するためのライブラリを取得し、ライブラリフォルダに展開する。

なお、注意点として、Arduino UNOの場合はソフトウェアシリアルを使用する事になるので、27行目にある下記の1行をアンコメントしておくこと。

#define ESP8266_USE_SOFTWARE_SERIAL

2. ESP8266を Arduinoに接続する(RX、TXをピン0、1に)

接続する必要があるのは合計5本。Arduino UNOは5V駆動、EPS8266は3.3V駆動なので、RX/TXに分圧抵抗等を入れている例もネットにはある。本当は入れたほうがいいのだけれど、私は入れていない。心配な方はどうぞ。

www.mauroalfieri.it

2. ESP8266のbaud rateを9600bpsに設定する

Arduinoにはシリアル端子が付いているのだけれど、この端子はデバッグ時に使用したいので無線モジュールとはソフトウェア的にシリアル通信をさせたい。しかしながら、ESP8266のデフォルトボーレートは115200bpsで、Arduino UNOのソフトウェアシリアルは115200bpsに対応出来ない。厳密には設定自体は出来るのだけれど、ビットエラーが多く使いものにならない。そこで、ボーレートを9600bpsまで落として使用する事にする。

Arduinoにからのスケッチをアップロードして、シリアルコンソールを出し、下記のコマンドを打てば良い。

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

3. ArduinoとESP8266の接続を変更する

RX、TXをピン12、11などに接続を変更する。これはソフトウェアシリアルを使用するためであるけれど、特にどこのピンでも問題無いように思われる。

4. Arduinoでコードを書く

ほぼライブラリのexampleからのコピペなのだけれど、下記のようなコードを書く。SSIDとパスワードは環境に合わせて。
github.com

#include "ESP8266.h"
#define SSID "XXXX"
#define PASSWORD "XXXX"

SoftwareSerial mySerial(12, 11); /* RX:D12, TX:D11 */
ESP8266 wifi(mySerial);
void setup(void)
{
Serial.begin(9600);
Serial.print("setup begin\r\n");

Serial.print("FW Version: ");
Serial.println(wifi.getVersion().c_str());


if (wifi.setOprToStation()) {
Serial.print("to station ok\r\n");
} else {
Serial.print("to station err\r\n");
}

if (wifi.joinAP(SSID, PASSWORD)) {
Serial.print("Join AP success\r\n");
Serial.print("IP: ");
Serial.println(wifi.getLocalIP().c_str());
} else {
Serial.print("Join AP failure\r\n");
}

Serial.print("setup end\r\n");
}

void loop(void)
{
}

これでArduinoあらWiFIが使用出来るようになる。書いて見ると簡単なのだけれど、ボーレート設定の所に気が付かず、半日以上かかってしまった。どうやら以前出荷されたESP8266はデフォルトボーレートが9600bpsになっていたようで、繋げばすぐに使える状態だったようです。最近のもの、と言ってもいつごろからかはわからないのだけれど、デフォルトが115200bpsになっているようで、Arduino UNOからは使用しにくくなってしまったという事らしい。

Ubuntu 16.04 Xenial XerusをVagrantで試す

Ubuntu vagrant

Ubuntuは半年に一度と比較的頻繁にリリースされており、常に最新の環境を使いたいと言うコアなユーザーには良いのだけれど、各リリースのサポート期間が9ヶ月と非常に短いため、安定した環境を長く使いたいと言うユーザには向いて居ない。そのため、通常のリリースとは別に約二年間に一度、LTS(Long Term Support)版がリリースされている。先日、その最新のLTS版であるUbuntu 16.04 Xenial Xerusがリリースされたので、この機会に早速試して見ることにした。

thinkit.co.jp

とは言え、新しいPCを用意してインストールする程の体力はなかったのでVagrant上に、である。

最初に用意した環境は下記の通り。

Mac OS El Capitan 10.11.4
Vagrant 1.8.1
VirtualBox 5.0.18

下記のようなコマンドでインストールと起動を行う。

mkdir xenial64
cd
vagrant init ubuntu/xenial64; vagrant up --provider virtualbox

しかし、下記のようなメッセージが出て起動しない。

vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'ubuntu/xenial64' is up to date...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: bridged
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: ubuntu
    default: SSH auth method: password
The guest machine entered an invalid state while waiting for it
to boot. Valid states are 'starting, running'. The machine is in the
'gurumeditation' state. Please verify everything is configured
properly and try again.

If the provider you're using has a GUI that comes with it,
it is often helpful to open that and watch the machine, since the
GUI often has more helpful error messages than Vagrant can retrieve.
For example, if you're using VirtualBox, run `vagrant up` while the
VirtualBox GUI is open.

The primary issue for this error is that the provider you're using
is not properly configured. This is very rarely a Vagrant issue.

しかも2回目以降はこうなる。

vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'ubuntu/xenial64' is up to date...
==> default: Clearing any previously set forwarded ports...
There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.

Command: ["modifyvm", "43be4d09-5cca-4db0-82e7-6b57265af2f3", "--natpf1", "delete", "ssh"]

Stderr: VBoxManage: error: The machine 'ubuntu-xenial-16.04-cloudimg' is already locked for a session (or being unlocked)
VBoxManage: error: Details: code VBOX_E_INVALID_OBJECT_STATE (0x80bb0007), component MachineWrap, interface IMachine, callee nsISupports
VBoxManage: error: Context: "LockMachine(a->session, LockType_Write)" at line 493 of file VBoxManageModifyVM.cpp

なんだこれはと思って、VirtualBox Managerで確認して見たところ、Guru Meditationと表示されている。
f:id:mikio-k:20160426020406p:plain
よくわからないがとりあえず、起動していない事は間違いない。VirtualBox Managerから仮想マシンを殺す事も出来なかったのでvagrant haltコマンドで終了させる。海外の掲示板などで見てみると、解決方法としてはVirtualboxを5.0.16にダウングレードすれば良いとのこと。

Ubuntu 16.04 (Xenial Xerus) | Hacker News

とりあえずietualBoxを5.0.16にダウングレードしてみることにした。そしてvagrant upした結果がこちら。

vagrant up

Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'ubuntu/xenial64' is up to date...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: bridged
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: ubuntu
    default: SSH auth method: password
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!

/sbin/ifdown eth1 2> /dev/null

Stdout from the command:



Stderr from the command:

sudo: unable to resolve host ubuntu-xenial
mesg: ttyname failed: Inappropriate ioctl for device

下記の部分で非常に時間がかかっていることと、

default: SSH auth method: password
default: Warning: Remote connection disconnect. Retrying...
default: Warning: Remote connection disconnect. Retrying...
default: Warning: Remote connection disconnect. Retrying...

下記のメッセージが気になると言えば気になるのだけれど、vagrant sshコマンドで無事にsshログインする事が出来るようになった。

The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!

/sbin/ifdown eth1 2> /dev/null

Stdout from the command:



Stderr from the command:

sudo: unable to resolve host ubuntu-xenial
mesg: ttyname failed: Inappropriate ioctl for device

Webブラウザからインターネット越しにArduinoのシリアル入出力を叩く

JavaScript Arduino Azure

ネットワーク越しにArduinoを操作したいなと思ったので、ちゃちゃっと作って見ました。需要はあんまりない気がするので、最低限の記述だけれおd,ほぼリアルタイムの通信が出来ているので、備忘録代わりに。

仕組みとしては下記のような感じ。
f:id:mikio-k:20160310070354p:plain
Arduinoのシリアル出力をMacで動くNode.jsが受信してSocket.ioでAzureに投げつける。WebブラウザとAzureも同様にSocket.ioで通信をしている。コードを見てもらったほうが早いと思うので下記に示す。まず、下記がMac上のNode.jsのコード。

var serialPort = require("serialport")
var io = require('socket.io-client')

// send server
socket = io.connect('http://ほげほげ.azurewebsites.net/');
socket.on('connect', function () {
	  console.log("socket connected");
});
  
socket.on('s2c_message', function (data) {
	  console.log("data receive:" + data.value);
	  sp.write(data.value);
});
  var flag = 0;
  var sp = new serialPort.SerialPort("/dev/tty.usbmodem1421", {
  baudrate: 9600,
  dataBits:8,
  parity:'none',
  flowControl:false,
  parser:serialPort.parsers.readline('\n')
});

sp.on('data', function(data) {
	console.log('data received: ' + data);
    socket.emit("c2s_message", { value : data });
});

sp.on("open", function () {
  console.log('open');
});

そしてこれがAzure WebApps上のコード

var http = require('http');
var socketio = require( 'socket.io' ); // Socket.IOモジュール読み込み
var fs = require( 'fs' ); // ファイル入出力モジュール読み込み
//Lets define a port we want to listen to
var port = process.env.PORT || 1337;
var ip = "0.0.0.0";

// 3000番ポートでHTTPサーバーを立てる
var server = http.createServer( function( req, res ) {
    res.writeHead(200, { 'Content-Type' : 'text/html' }); // ヘッダ出力
    res.end( fs.readFileSync('./index.html', 'utf-8') );  // index.htmlの内容を出力
}).listen(port);

// サーバーをソケットに紐付ける
var io = socketio.listen( server );

// 接続確立後の通信処理部分を定義
io.sockets.on( 'connection', function( socket ) {

    // クライアントからサーバーへ メッセージ送信ハンドラ(自分を含む全員宛に送る)
    socket.on( 'c2s_message', function( data ) {
        // サーバーからクライアントへ メッセージを送り返し
        io.sockets.emit( 's2c_message', { value : data.value } );
    });
});

最後にブラウザのUI

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>
    #messageForm {
        margin-top: 15px;
    }
    </style>

    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
    <script type="text/javascript" src="socket.io.js"></script>
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form name="form">
                    <div class="form-group">
                        <input  name="field" type="text" class="form-control" id="messageForm" style="width:100px;">    
                    </div>
                    <div class="form-group">
                        <button type="button" class="btn btn-primary" id="sendMessageBtn">send message</button>
                    </div>
                </form>
                <div id="messageView"></div>
            </div>
        </div>
    </div>

    <script type="text/javascript">
	document.form.field.focus();
    var ioSocket = io.connect( "http://arduinosocket.azurewebsites.net/" ); // チャットサーバーに接続
    // サーバーからのデータ受け取り処理
    ioSocket.on( "connect", function() {} ); // 接続
    ioSocket.on( "disconnect", function() {} ); // 切断

	$("#messageForm").keydown(function(e) {
            if ((e.which && e.which === 13) || (e.keyCode && e.keyCode === 13)) {
				        var message = $("#messageForm").val();
    				    $("#messageForm").val("");

				        // クライアントからサーバーへ送信
        				ioSocket.emit( "c2s_message", { value : message } );

                return false;
            } else {
                return true;
            }
			});
    // サーバーからクライアントへの送り返し
    ioSocket.on( "s2c_message", function( data ) { appendMessage( data.value ) });

    // 画面にメッセージを追記
    function appendMessage( text ) {
        $("#messageView").prepend( "<div>" + text + "</div>" );
    }

    // 自分を含む全員宛にメッセージを送信
    $("#sendMessageBtn").click( function() {
        // メッセージの内容を取得し、その後フォームをクリア
        var message = $("#messageForm").val();
        $("#messageForm").val("");
        // クライアントからサーバーへ送信
        ioSocket.emit( "c2s_message", { value : message } );
    });

    </script>
</body>
</html>

起動直後に自身のIPを外部に投げつける

JavaScript node.js BeagleBone Black Rasberry Pi

BeagleBone BlackだとかRaspberry Piだとか、画面の無いLinux機器を外部から操作しようと思う場合、SSH接続してコマンドを叩く、ってのが一般的かなと思うのですが、SSH接続をしようにもその機器のIPアドレスを知らない事にはどうにもならないわけです。

ボンジュールなどのmDNSでも良いんですが、端末によっては上手く動かない事等もあったため不便を強いられて居ました。と言うことで、起動直後に自身に割り当てられたIPを取得して外部に立てたサーバに投げつけるスクリプトを書きましたので備忘録です。

まず、下記のようなnode.jsスクリプトを作成します。

var os = require('os');
var http = require('http');
console.log(getLocalAddress());
var req = http.get("http://サーバのアドレス/device/端末名/ip/" + getLocalAddress());
function getLocalAddress() {
    var ifacesObj = {}
    var interfaces = os.networkInterfaces();
    if(interfaces.hasOwnProperty("wlan0")){
        interfaces["wlan0"].forEach(function(details){
            if(details.family == "IPv4"){
                ifacesObj = details.address;
            }
        })
    }
    return ifacesObj;
};

ここで作成したnode.jsスクリプトには実行権限を与えるのを忘れな異様にして下さい。

/etc/rc.localに下記の一行を追記します。

nodejs 上記で作成したファイルへのパス

以上です。ちなみにサーバ側のコードはどんなんでも良いんですが、こんな感じで動くかと思います。

var http = require('http');
var port = process.env.PORT || 1337;
var ip = "0.0.0.0";
http.createServer(function(req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
	if(req.url.match(/ip/)){
		ip = req.url;
		res.end('IP: ' + ip);	
	}else{
		res.end('IP: ' + ip);
	}
}).listen(port);

BeagleBone BlackとRaspberry Piで動作確認をしていますが、おそらく他のものでもそのまま、もしくは大きな修正はなく動くんじゃないかと思います。

Wordpressからはてなブログへの引っ越しに伴うURL変更と301リダイレクト

Wordpressからはてなブログに引っ越ししました。今回はその手順について覚書。と言っても基本的には、下記のサイトだとかを参考にしてもらえれば概要はわかるんじゃないかなと思うので、下記記事にない、ブログ移転に伴うURL変更への対策について。

www.junpeihazama.com

今回行った作業は下記の通り。

  1. Wordpressで記事をXMLとしてエクスポートする
  2. はてなブログ側でそのXMLをインポートする
  3. ドメインの設定を行う
  4. URLが変わってしまったので、リダイレクトの設定を行う

ここで、ドメインの設定を行うためには、はてなブログのProにする必要がある。Proの料金は1ヶ月約1000円、2年払いだと月600円になる。そこまで高くは無いのだけれど、このためだけにProにすべきかは悩むところである。しかしながら、お金を払ってしまえば頑張って更新するような気もするので、これぐらいなら良いかな?って思ってしまったのも事実。

また、記事中の画像に関しては、はてなフォトライフにアップロードし直す方法も紹介されていたりするのだけれど、私は元々のサーバから画像を全部引っ張ってくることにした。いくつか方法が紹介されていたのだけれど、何故か私の場合うまく行かなかったのです。時間があったら将来的に対応するかも知れないけれど。

さて、問題は最後のリダイレクト設定。今回、はてなブログに引っ越しを行うにあたり、URLが変更になっており、このままだと古いURLにアクセスした人には何も表示されなくなってしまうし、何よりグーグルなどで検索してきてくれた人に申し訳ない。そこで、古いURLにアクセスが有った時に、自動的に新しいURLにリダイレクトさせたい。そしてこのリダイレクトには大きく2つの前提条件がある。箇条書きすると下記の通り。

  • ブログのURLがhttp://kur.jpからhttp://blog.kur.jpに変更になっている。
  • ブログ以外(例えば私の自己紹介とか、ポートフォリオなどの固定ページや、画像など)のコンテンツは、http://kur.jpに残す

上記を実現するために、サーバーの.htaccessに下記の内容を書いた。

#php_flag mbstring.encoding_translation off
#php_value default_charset "UTF-8"

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^20.* http://blog.kur.jp/entry%{REQUEST_URI} [R=301,L]
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

今回私が追加したのは、下記の部分。

RewriteRule ^20.* http://blog.kur.jp/entry%{REQUEST_URI} [R=301,L]

私の場合、wordpressのブログコンテンツは、URLに年月日が必ず含まれており、(ドメイン名)/2015/10/03/のようになっていたので、最初の2文字が20の場合はブログ記事へのアクセスだと判断してはてなブログに301リダイレクトすることにした。なお、wordpressは.htaccessを気軽に書き換えに来るので、パーミッションの変更を行って、wordpressから.htaccessを書き換えられないようにしている。

このように301リダイレクトしておくと、Googleのクローラなどはコンテンツが移動されたとみなしてインデックスを修正してくれるらしい。事実、いくつかの記事タイトルでGoogleで検索してみると、移転直後は元のURLがインデックスされていたものが現在でははてなブログのURLが表示されている。問題は、引っ越ししたものの、ほんとに更新するのか?と言うところであるのだけれど。

自宅のインターネット回線を光ファイバからWiMAXに

internet mobile 日記

自宅のインターネット回線を光ファイバをWiMAXに変更しました。光ファイバを解約する際には色々と不安もあったものの、結論から書くと相当満足しています。月々の回線維持費が実質2000円程度で済むこと、家でも外でも同じ環境でインターネットが利用出来るなど利便性も上がっています。速度はまぁそれなりですが、意外とこれでもなんとかなるもんです。

以下、そもそも何故、光ファイバからWiMAXに変えようと思ったのかの経緯とか、光ファイバからWiMAXに変えて良かった点、悪かった点を簡単にまとめてみます。

続きを読む

Xperia Z1 f SO-02Fを買って一週間ほど使ってみました。

android mobile xperia

同じくXperiaのAcro HD SO-03Dからの乗り換えです。Acro HDの発売が2012年3月、Z1 fの発売が2013年12月なので、前回機種変更してから約2年経っている事になります。ちなみにAcro HDを買った時にはXperia AcroHDを一ヶ月ぐらい使ってみて思ったことというエントリを書きました。

前回同様、しばらく使ってみたので感想を書いてみます。もっとも前回のように発売直後に購入した場合とは異なり発売から半年程度経ってからの購入なので、インターネット上にレビュー記事は腐るほどあるのですが。

続きを読む