0

mbedで取得したセンサーの値をsinatra+tokyocabinetで保存する

まずセンサーのデータを時系列で溜めたり、取り出したりするためのwebサービスをsinatra+TokyoCabinetで作っておいた。


そこにmbedのADコンバータでCdSの明るさを計測、LEDを点灯/消灯mbedにEthernetを接続しtwitterにpostするを合体させ、約10秒間隔でセンサーの値をhttp-postしまくった。

一度web APIが出ている場所にマイコンの情報も保存しておけば、あとでマッシュアップできて便利になる。このAPIの下にmbedでもgreasemonkeyスクリプトでも何個でもぶら下げられる。
mbedからwedataに保存しても良かったんだけど、あえて自分で作ってみた。



■mbedで明るさを計測してhttp-postする
作った回路。CdSで明るさを取得し、LEDで明るさを表示する。mbedのRD+,RD-,TD+,TD-ピンにトランス入りEthernetジャックを接続し、LANケーブルを刺した。
mbed ethernet


プログラム。CdSに入る光量が少なくなると緑色のLEDが光る。http-postはTickerで定期的に呼び出したかったが、Ticker内でHTTPClient.hの関数を使うとmainに戻ってこなくなるので仕方なくmain内で20ループ毎(約10秒毎)にHTTPClient.post()するようにした。stdは使えるけど、pthread等のスレッド機能は無いみたいなのでこれは痛い。
sensor_http_post | mbed
#include "mbed.h"
#include "HTTPClient.h"
#include <string>
using namespace std;

Serial pc(USBTX, USBRX);

DigitalOut led1(LED1); // blink
DigitalOut led2(LED2); // blink when http-post
DigitalOut led3(p11); // represent ADC value

AnalogIn adc(p15);
float ain;

HTTPClient http; // use DHCP

const string api_uri = "http://shokai.mag.keio.ac.jp/sensor-storage/shokai/cds/";
const string mbed_name = "shokai-mbed01";
const string sensor_name = "CdS";

void blink_led(DigitalOut led, int num){
    for(int i=0; i < num*2; i++){
        led = !led;
        wait(0.1);
    }
}

void post_sensor_value(){
    blink_led(led2, 2);
    char query[256] = "";
    char result[4096] = ""; // 4kb
    sprintf(query, "name=%s&%s=%f", mbed_name.c_str(), sensor_name.c_str(), (float)ain);
    pc.printf("post:%s => %s\n", query, api_uri.c_str());
    http.post(api_uri.c_str(), query, result, 4096);
    pc.printf("%s\n", result);
    blink_led(led2, 2);
}

int main(void){
    led1 = 1;
    int count = 0;
    while(1) {
        ain = adc;
        pc.printf("sensor:%f\n", float(ain));
        led1 = !led1; // blink LED
        if(ain > 0.1) led3 = 0; // represent CdS value
        else led3 = 1;
        if(count > 20){
            post_sensor_value();
            count = 0;
        }
        count++;
        wait(0.5);
    }
}
せっかくC++が使えて、mbed.hのAPIはC++っぽいのに、HTTPClient.hはそうでもないみたいだ。
mbedのローカルのストレージがfopenで読めるので、そこに設定ファイルを置けるようにしたい。yamlやjsonをparseするライブラリを作りたい。



動作はシリアル通信で見れる。表示にはserialterm.rbを使った。
postすると返り値がjsonで来る。
mbed http-post



■tokyocabinetとsinatraでデータを溜めるAPIを作る
できたもの
sensor-storage API


githubでソースを全部公開しておいた。
shokai's sensor-storage at master – GitHub


http://shokai.mag.keio.ac.jp/sensor-storage/ にhttp-postした内容を、json stringにエンコードして時刻_マイクロ秒をキーにしてTokyoCabinetに保存する。
keyの _ の左側が時刻になっているので後で使う時に便利。

http://shokai.mag.keio.ac.jp/sensor-storage/keys で保存されているkeyの一覧がJSON形式で見れる。
http://shokai.mag.keio.ac.jp/sensor-storage/recentで最近10件のkeyとvalueが取れる。他にもrecent/件数や、last(最後に入れたデータ)、count(データ件数)などのAPIがある。


今回のmbedでCdSの値を保存しているのは http://shokai.mag.keio.ac.jp/sensor-storage/shokai/cds/recent になる。shokai/cds/last , shokai/cds/recent , shokai/cds/count などで色々データが取り出せる。

/ 以下に適当に文字列を付けると新しくDBを作るようになっている。もしデータをpostしてみたいなら、mbedの方のコードは/shokai/cds/をハードコーディングしているので、そこを適当に変えてから使ってくれるとうれしい
あまりにも大きいデータは入らない。


スクリプト自体はわりとシンプル。sinatra使って1ファイルに書いてある。
server/main.rb at master from shokai's sensor-storage – GitHub
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rubygems'
require 'sinatra'
require 'sinatra/reloader' if development?
require 'rack'
require 'json'
require 'tokyocabinet'
include TokyoCabinet

@@dbdir = 'db'

def db_open(dbname='/')
  dbname = dbname.to_s.gsub(/\//, '_')
  @hdb = HDB.new
  Dir.mkdir(@@dbdir) if !File.exists?(@@dbdir)
  @hdb.open("#{@@dbdir}/_#{dbname}.tch", HDB::OWRITER|HDB::OCREAT)  
end

after do
  @hdb.close if @hdb
end

get '*/' do
  path = "../" * params[:splat].first.count('/')
  redirect "#{path}./readme"
end

get '*/keys' do
  db_open(params[:splat])
  @hdb.keys.reverse[0...10000].to_json
end

get '/dbs' do
  Dir::glob("#{@@dbdir}/*.tch").map{|i|
    name = i.to_s.scan(/.*\/(.+)\.tch/).first.to_s.gsub(/_/,'/')
    tmp = name.split(//u)
    tmp.shift
    tmp.to_s + "/"
  }.uniq.delete_if{|i|
    i =~ /\/\//
  }.sort.to_json
end

get '*/count' do
  db_open(params[:splat])
  {"count", @hdb.rnum}.to_json
end

get '*/last' do
  db_open(params[:splat])
  key = @hdb.keys.last
  {key, @hdb[key]}.to_json
end

get '*/recent' do
  db_open(params[:splat])
  @hdb.keys.reverse[0...10].map{|k| {k, @hdb[k]} }.to_json
end

get '*/recent/:num' do
  db_open(params[:splat])
  num = [params[:num].to_i, 1000].min
  @hdb.keys.reverse[0...num].map{|k| {k, @hdb[k]}}.to_json
end

get '*/:tc_key' do
  db_open(params[:splat])
  key = params[:tc_key]
  {key, @hdb[key]}.to_json
end

post '*/' do
  db_open(params[:splat])
  now = Time.now
  key = "#{now.to_i}_#{now.usec}"
  params.delete(:splat.to_s)
  @hdb.put(key, params.to_json)
  {key, @hdb[key]}.to_json
end

delete '*/:tc_key' do
key = params[:tc_key]
  v = @hdb[params[:tc_key]]
  @hdb.out(params[:tc_key])
  {key,v}.to_json
end


githubのmiscディレクトリ以下にテスト用にhttp postしたりするためのスクリプトがある。mbedが無い時の動作チェックにどうぞ。


サーバーはthinで10個preforkさせておいて、それぞれ別のportで起動させてある。apache2のmod_proxy_balancerでそこにproxyして、80番ポートのサブディレクトリ下にあるように見せている。



0

mbedにEthernetを接続しtwitterにpostする

@1VQ9の助力を得てmbedがethernetにつながった。1VQ9はパルストランスも通さず、分解したLANケーブルをmbedに直接差し込んで動かしてたけど、俺はmbed Ethernet Testingに従ってパルストランス入りのEthernet jackを使った。



ethernetにつないだところ
mbed + ethernet
mbedとethernet jackは
  • mbed p1 = Ethernet RD+
  • p2 = RD-
  • p7 = TD-
  • p8 = TD+
という風に接続した。こっちでは別のつなぎ方をしているが、この配置だと動かなかったのでjack毎に違うのかもしれない。よくわからない。


配線はこっちの方が見やすいかも
mbed + ethernet


裏のピン配置がブレッドボードに刺せるピッチ幅ではなかったので、秋月で100円で売ってる両面ガラススルーホール基盤の切れ端とピンヘッダを駆使してピン配置を変更した。
ethernet jack for breadboard
ethernet jack for breadboard


mbedにはHTTPClientというDNSやDHCPやHTTPを適当に解決してくれるモジュールがある。
ほぼmbed EMAC/HTTPClientのまま動いた。
HTTPClientを使うために、
http://mbed.org/projects/cookbook/svn/EMAC/lwip/trunk
をprojectにimportする必要があった。mbedのエディタのGUIからimportできた。

tweet_test | mbed
#include "mbed.h"
#include "HTTPClient.h"
using namespace std;

DigitalOut led(LED1);

HTTPClient http; // use DHCP

/* // use static IP
HTTPClient http("mbed", // hostname.
                IPv4(192,168,1,39), // IPv4 address
                IPv4(255,255,255,0), // netmask
                IPv4(192,168,1,1), // default gateway
                IPv4(192,168,1,1)); // dns server
/**/

const char msg[] = "status=mbed test";

int main(void) {
    http.auth("username", "password");
    http.post("http://twitter.com/statuses/update.xml", msg);
  
    while(1) {
        led = !led;
        wait(0.2);
    }
}
twitter APIにbasic認証を通ってhttp-postし、成功するとLED点滅のループに入る。

twitter APIは同じtweetを連続で受け取らないようになっているので、このプログラムだと一度tweetした後はmsgを別の文字列に変えないと再確認できない。後ろに乱数とか時刻とかをつける必要がある。

0

mbedのADコンバータでCdSの明るさを計測、LEDを点灯/消灯

mbedのAnalogInとDigitalOutとSerial通信の機能を使った。
CdS+LED on mbed


CdSを手で隠すとLEDが光るようにした。
CdSは光に反応して抵抗値が変わるセンサー。mbedに6ポートあるADコンバータでCdSの電圧を計測すると、明るさがわかる。
CdS+LED on mbed

プログラムはcds_led_serial | mbedにpublishした。
mbedのアカウントを持っている人ならすぐに右上のImport program into Compilerというリンクからいじれる。


動作確認用に基板上のLED1は0.3秒間隔で点滅させつつ、p15のADCの値を見てブレッドボード上のLED2を点灯/消灯させるコード

#include "mbed.h"

Serial pc(USBTX, USBRX);

DigitalOut led1(LED1); // on board LED
DigitalOut led2(p11);
AnalogIn adc(p15);
float ain;

int main() {
    while(1) {
        led1 = !led1; // blink LED
        ain = adc;
        if(ain < 0.4) led2 = 1;
        else led2 = 0;
        pc.printf("%f\r\n", (float)ain);
        wait(0.3);
    }
}
C++はboostばかり使っていたので、printfを久しぶりに使った。
他のサンプルコードを読んでるとたまに上の方でusing std;している物もあるので、mbedでstdは使えるんだろうな多分。

ADコンバータのレジスタへの操作はAnalogInのオブジェクトを作った時点で隠蔽されてるけど、変数ainとして作ったAnalogInのオブジェクトを読む毎にAD変換が行われるんだろうか??
たぶん行われる気がするので、今回は一度値を別のint型変数に代入するようにした。



回路図。今回使った小さいCdSは、光の状態で変動するがだいたい1kΩ前後の抵抗値を持っている。
GNDとmbedのADコンバータ(15番ピン)の間に同じく1kΩ程度の抵抗を入れて分圧し、電圧を計測した。
CdS+LED on mbed


mbedはSerialライブラリでパソコンと通信できる。MacやLinuxなら特にドライバ等必要なしに、/dev/tty*にusbmodemとして認識されているはず。windows用にはCOMポートとして認識できるドライバが公開されている。
シリアル通信をモニタしてみると、CdSからの電圧を計測している15番ピンは0.1〜0.6の間の値を出力している事がわかった。
mbedのプログラム中で、ADCが0.4以下の値の時にブレッドボード上の緑色のLED(led2)を点灯させ、逆の時は消灯するようにした。
mbed-adc-serial


シリアル通信のモニタにはruby-serialportで作ったterminalを使っている


0

ArgsParserをrubygemsに登録した

車輪の再発明臭がヤバイが、コマンドラインの引数のパーサを作ったのでrubygemsに登録した。はじめてgem登録した。

gemでインストールできる。

gem update --system
gem install ArgsParser


newgemコマンドでgemを作ってrubygems.orgに登録し、一応rspecでtestも書いた
gemはrubyforge.orgで公開するのが普通だったけど、2年ぐらい前からgithubでgemが作れる様になったので新しいプロジェクトはみんなgithubに行って、2009年秋?頃にgithubでgemがビルドできなくなったからかrubygems.orgでやってねという事になったらしい。


なのでリポジトリはいつも使っているmercurialにしてbitbucket.orgで公開する事にした。
shokai / argsparser-ruby / overview — bitbucket.org


既にgemにはいくつかCのgetopt風なARGVのparserがあるけど、なんとなくインタフェースが好きじゃないので自分が欲しい物を作った。

  1. –help -debug等のオプションの有無を判別する
  2. -x 320 -y 240 等のパラメータ(key value)を取得する
  3. -hと-helpのように省略名称が関連づけられていれば同じ物として扱う
  4. -helpと—helpのようなハイフンの数の違いを無視して、全て :help でアクセスできる
  5. ruby math.rb add -a 10 -b 25 の”add”のような、第一引数を取り出す
  6. パラメータ名にコメント文を付ける(-helpで起動した時の引数の説明文に使う)
という機能に絞ってある。



parseすると、第一引数(String)とパラメータ(Hash)が返ってくる。第一引数がない(いきなりパラメータが来た)場合は、第一引数はnilになる。
example.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'ArgsParser'

# 必要なパラメータ名を登録する
parser = ArgsParser.parser
parser.bind(:help, :h, "show help") # name, shortname, comment for help
parser.bind(:frame, :f, "frame image (required)")
parser.bind(:message, :m, "message (required)")
parser.bind(:size, :s, "size (required)")
parser.comment(:min, "minimum size") # add comment for help
parser.comment(:max, "maximum size")
parser.comment(:debug, "debug mode")


# parseして、引数が足りてるか、help表示指定が無いかチェック
first, params = parser.parse(ARGV)

if parser.has_option(:help) or !parser.has_params([:frame, :message, :size])
  puts parser.help
  puts 'e.g.  ruby example.rb -f frame.png -m "hello world" -s 320x240 -debug'
  exit 1
end

if first
  puts 'first arg : ' + first # 第一引数
end

if parser.has_param(:size)
  puts 'size : ' + params[:size] # -sでも-sizeでも :size でアクセスできる
end

# 全ての引数を表示
p params


実行してみる
ruby example.rb hoge -f frame.png -m "hello world" -s 320x240 -debug
結果
first arg : hoge
size : 320x240
{:message=>"hello world", :size=>"320x240", :debug=>true, :frame=>"frame.png"}

1

mbed NXP LPC1768を買った

rain_yukizoraが東京に来ていて、2人でスタバでmbedをいじったりした。
Basic StampやGainerのように、ブレッドボードに刺す事を前提にピンが裏に出ている。でもその時はブレッドボードを持ち歩いて無かったので、基板上のLEDとシリアル通信等を試した。ピンク色の物体は鞄の中でピンが折れないように基盤裏につけてる梱包材。

R0014222.JPG

mbedはエディタとコンパイラがwebサービス側にあり、開発は全てwebブラウザ上で行う。そしてできたバイナリをダウンロードしてマイコンに書き込み、実行させるという謎の16bitマイコンボード。

開発言語はC++。どうやら高価な商用コンパイラとライブラリ群がサーバー側にあるらしい。
例えばHTTPClientのライブラリがあり、DNSなども解決してくれるのでtwitterに認証してtweetするとかが簡単にできたりする。40円ぐらいで売ってるEhternetコネクタをそのまま接続するだけでLANケーブルが刺せるようになるとか、既にいろいろ揃っている。Audio IOやリチウムポリマー電池を使うための回路もボード上にあるように見える。


mbed.orgのトップページに貼られているビデオを見れば雰囲気がわかる。40秒ぐらいで短くてわかりやすいし、センスがいい。



■購入
スイッチサイエンスで6000円台後半で買った。秋月では6000円で売ってるけど、スイッチサイエンスで物を買った事が無かったので買ってみた。注文した翌日に届いて驚いた。
秋月の方は「評価キット」という名前でCDや箱やケーブルなどが付いてきて得な様に見えるけど、スイッチサイエンスの方にもちゃんと付いてた。

しばらくの間は6000円ぐらいで、そのうち1万円ぐらいに値上げするらしい。



■アカウントを取る
mbedをUSBケーブルでMacに接続すると、USBドライブとしてマウントされる。(Windowsでも同様)


開くとMBED.HTMというファイルが入っている
ピクチャ 1


webブラウザが開き、アカウント登録画面に行く。この画面は普通に誰でも見れるけど、USBデバイスとしてmbedが刺さっていないとsignupできないようになっている。どうやっているのかな・・・


アカウントを取るとこんなプロフィールページができる。publishしたnotebookやプログラムが見れる。
http://mbed.org/users/shokai/



■コードを書く
ログインして右上のcompilerのリンクから、IDEが開く。新規プロジェクト作成して、handbookを参考にしながらコードを書いてみた。
まず新規プロジェクト作成した時点でとりあえずLEDを一つ0.5秒間隔で点滅させるプログラムが書かれているので、ああこういう感じなのか〜とすんなり入れる。
ピクチャ 4

main.cpp

#include "mbed.h"

Serial pc(USBTX, USBRX); // tx, rx

DigitalOut myled1(LED1);
DigitalOut myled2(LED2);

int main() {
    while(1) {
        myled1 = 1;
        myled2 = 0;
        pc.printf("led 1 on\r\n");
        wait(0.5);
        myled1 = 0;
        myled2 = 1;
        pc.printf("led 2 on\r\n");
        wait(0.2);
    }
}
左上の「Compile」ボタンを押すとコンパイルされる。エラーが無い場合、いきなりプログラムがダウンロードされる。



■プログラムを書き込み、実行する
USBドライブとしてマウントされてるmbedにbinを投げ込んで、基板上のリセットボタンを押すと実行される。
ピクチャ 2


基板上のLEDが点滅すると同時に、シリアル通信もちゃんと来ていた。
1つのUSBコネクタだけど、USBストレージと /dev/tty.usbmodem622 の2つのデバイスとして同時に認識されている。
ピクチャ 7
MacやLinuxではドライバは必要ない。WindowsもSerialPC – handbook – Tracにあるドライバをインストールすれば使えるらしい。


■プログラムを公開する
エディタ画面でプロジェクト名を右クリックして「publish」ができる。プロジェクト名に – (ハイフン)が入っているとHTTPステータスコード500でconnection errorというのが出て、publishできない。 _ (アンダーバー)を使った。
ピクチャ 5

公開したプログラム
led_serial | mbed

コードがブラウザ上で見れない。まあそのうち改良されるだろう。
コメントや星を付けれるし、「import program into compiler」を押せば他人のプログラムでもすぐ自分で編集できる。
importは、他にもtarで固められたファイルかSVNのリポジトリのアドレスを指定する事もできる。



■感想
便利なライブラリが揃っていて、現状でもう十分面白いボードになっていると思う。

あとはネットワーク上にあってユーザアカウントがあるのだから、もっと色々できそう。組み込み系は開発効率よりもコミュニケーション効率の低さがまずいと思うので、web上にエディタがあるという事がそのへんをどうにかする手段になりそう。

人の作ったコードをimportするだけではなくライブラリとして使えるようになると思うので、何かRSSのparserとかjsonのシリアライザとかから作っていってみるとリソースが積み重なって面白い事になるかも。