digimesh化したxbeeを,以下のような流れで通信テストした.
- 遠隔地に設置したarduinoで温度を計測
- 計測値は,各所に配置されたxbeeを経由し,ホスト側となるEthernet Shieldを積んだarduinoへ送信
- 測定結果を受けとったarduinoは,所定のサーバーに数値をアップデート
今回のテストで使用したものは,ざっと以下のとおり.
- arduino
- Ethernet Shield
- Xbee Shield
- LM35DZ(高精度IC温度センサ)
- Xbee 802.15.4モジュール(シリーズ1)
Xbeeのdigimesh化
x-ctuにて,ファームウェアをXB24-DMに書き換えることで使用できる.
詳しくというか,メモが以下にある.
※メモ
テスト時点では,最新ファームは8003であったが,現在804Bがリリースされた模様.
今回のアップデートは,X-CTUで見る限り,I/Oとsleep周りが強化されたような感じがある.ただし,ドキュメントなどは未だ8003ベースなので,詳細は不明.
計測側arduinoのスケッチ

計測側のarduinoが行なうことは,温度を計測し,xbee経由でホスト側へ計測値を送信することとなる.
温度計測には,LM35DZを使用したが,これについては建築農業工作ゼミ2009: 温度センサを参考にした.
#include <stdio.h>
//ホスト側xbeeのアドレス
long addr64f=0x0013****;
long addr64l=0x4053****;
//温度関係(LM35DZ)
int a_pin=0; //analog入力pin
int a_val; //analog入力値(0~1023)
int a_ave;
float v=5; //基準電圧(V)
long tempc=0; //摂氏値(℃)
int nloop = 10;
void setup(){
Serial.begin(9600);
digitalWrite(13,LOW);
}
void loop(){
a_ave = 0;
//1Hzで10秒間測定
for(int i=0;i<nloop;i++){
a_val = analogRead(a_pin);
a_ave += a_val;
delay(1000);
}
//平均化
a_ave /= nloop;
//温度に変換
tempc=((v*a_ave)/1024)*10000;
//ホストへ値を送信
senddata(addr64f,addr64l,tempc);
}
byte getlength(long data){
byte buf = sizeof(long)+14;
return(buf);
}
byte checksum(long addr64f,long addr64l,long data){
byte buf;
for(int i=0;i<7;i++){
switch(i){
case 0: //API identifier
buf = 0x10;
break;
case 1: //Frame ID
break;
case 2:
for(int j=0;j<sizeof(addr64f);j++){
buf += lowByte(addr64f);
addr64f = addr64f>>8;
}
break;
case 3:
for(int j=0;j<sizeof(addr64l);j++){
buf += lowByte(addr64l);
addr64l = addr64l>>8;
}
break;
case 4:
buf += 0xFF;
buf += 0xFF;
break;
case 5:
buf += 0x08;
break;
case 6:
for(int j=0;j<sizeof(data);j++){
buf += lowByte(data);
data = data>>8;
}
break;
}
}
buf = 0xFF - buf;
return(buf);
}
void senddata(long addr64f,long addr64l,long data){
char str[100];
char strbuf[10];
Serial.flush();
delay(100);
Serial.print(0x7E,BYTE);
Serial.print(0x00,BYTE);
byte dl = getlength(data);
Serial.print(dl,BYTE);
Serial.print(0x10,BYTE);
Serial.print(0x00,BYTE);
print_longhex(addr64f);
print_longhex(addr64l);
Serial.print(0xFF,BYTE);
Serial.print(0xFF,BYTE);
Serial.print(0x00,BYTE);
Serial.print(0x08,BYTE);
print_longhex(data);
byte cs = checksum(addr64f,addr64l,data);
Serial.print(cs,BYTE);
Serial.flush();
delay(20);
return;
}
void print_longhex(long addr)
{
byte buf[4];
int i,n=4;
char str[2];
for(i=0;i<n;i++){
int j = n-i-1;
buf[j] = lowByte(addr);
addr = addr>>8;
}
for(i=0;i<n;i++){
Serial.print(buf[i],BYTE);
}
return;
}
ホスト側のスケッチ

ホスト側arduinoは,xbeeより受信したコマンドから測定結果のみを抽出し,Ethernet Shield経由でローカルサーバーにアップデートしていく.
基本的には建築農業工作ゼミ2009: Arduinoで計測した値を指定のwebサーバに送信、保存するを参考にした.
違いを出すという意味で,こちらはrailsで測定結果の表示部分まで作成してみた.
テスト中,xbeeで送られてくるコマンドがRF Dataのみではないことに気付いた.
とりあえず,oklablog – digimeshでの通信コマンドでメモしたとおり,Receive Packetの16byte以降を読むようにした.
(適当だ...)
#include <Ethernet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
byte mac[] = { 0x00, 0x50, 0xC2, 0x97, 0x**, 0x** };
byte ip[] = { 192, 168, 0, *** };
byte gateway[] = { 192, 168, 0, 1 };
byte subnet[] = { 255, 255, 255, 0 };
byte server[] = { 192, 168, 0, *** };
byte buf[30];
int flg=0;
int strn=0;
Client client(server, 3000);
void setup()
{
Ethernet.begin(mac, ip, gateway, subnet);
Serial.begin(9600);
delay(1000);
Serial.flush();
Serial.println("connecting...");
}
void loop(){
int c,buf[200];
int i,j;
int imax=20;
long data;
if(Serial.available()>imax-1){
i=0;
do{
c = Serial.read();
buf[i] = c;
i++;
}while(c != -1);
i--;
data = 0;
for(j=0;j<3;j++){
data += 256*(3-j) * buf[15+j];
}
data += buf[18];
Serial.println(data);
data_post(data);
}
delay(1000);
}
void data_post(long buf)
{
char str[100];
if(client.connect()){
Serial.println("connected");
sprintf(str,"%d",buf);
client.print("GET /push/push?id=");
client.print(str);
client.println(" HTTP/1.0");
client.stop();
}
else{
Serial.println("connection failed");
client.stop();
}
return;
}
railsで測定結果の表示

今までグラフといえばphp+jpgraphだったが,rubyでもGruffみたいなものがあるようで,データのポストと表示部分をrailsで作成した.
見よう見まねで作成したのでおかしいところがありそうだけど,とりあえず動いたのでよしとする.
しかし,phpで作成していたよりもシンプルなコードで書けるので驚いた.
今回を機に,phpからrubyへシフトしていこうかと思う.
push_controller.rb
class PushController < ApplicationController
def push
db = Product.new
db.data = params[:id]
db.save
@data = "ok!"
end
end
push.html.erb
<html>
<head>
<title>data push</title>
</head>
<body>
<%= @data %>
</body>
</html>
pict_controller.rb
class PictController < ApplicationController
def show
ndata = 10
buf1=Array.new(ndata,0)
buf2=Array.new(ndata,0)
db = Product.find :all,:order=>'id desc',:limit=>ndata
i=0
db.each {|id|
buf1[i]=(id.created_at+32400).strftime('%y/%m/%d %H:%M:%S')
buf2[i]=id.data.to_f/100
i=i+1
}
@time = buf1
@data = buf2
end
def image1
g=Gruff::Line.new 640
g.font = "/usr/share/fonts/japanese/TrueType/sazanami-gothic.ttf"
g.title = "計測結果 by arduino + xbee(digimesh)"
g.title_font_size=24
ndata = 200
g.x_axis_label = "計測時間"
g.y_axis_label = "温度"
g.hide_legend = true
g.hide_dots = true
g.maximum_value=40
g.minimum_value=10
buf = Array.new(ndata,0)
i = 0
db = Product.find :all,:order=>'id desc', :limit=>ndata
db.each {|id|
buf[ndata-1-i]=id.data.to_f/100
case i
when 0,ndata-1
g.labels[ndata-1-i]=(id.created_at+32400).strftime('%m/%d\n%H:%M:%S')
end
i=i+1
}
g.data("random data",buf)
g.theme_37signals
g.sort = false
g.margins=50
send_data(g.to_blob,:type=>'image/png',:disposition=>'inline')
end
end
show.html.erb
<center>
<%= image_tag url_for(:action=>'image1') %><br \>
</center>
<table border=2 width=300 align=center>
<tr bgcolor="$cccccc">
<th>計測時間</th>
<th>温度</th>
</tr>
<% for i in 0..@data.length-1 -%>
<tr align=center>
<th><%= @time[i] %></th>
<th><%= @data[i] %></th>
</tr>
<% end -%>
</table>
結論
とりあえず,当初計画していたことが一通りができた.
今回のテストを通して得た収穫は,以下のとおり.
- digimesh modeで複数のxbeeを経由して通信可能
- ただし,経由したデータは一部欠損が生じており,改良の必要あり
- ruby,railsが予想以上におもしろく,使える
今後は,2のデータ欠損を改善していくとともに,省電力稼動のためsleepを活用していく予定.
- Newer: Hatenaへ移行します
- Older: Chrome Keyconfig晒し