1Click飲みRomoCartTempescope色色[:iroiro]Other Projects

2012年1月18日水曜日

A scriptlet to let you access Wikipedia in the blackout

If you're desperate to access Wikipedia even during the blackout...
  1. Bookmark the following link: me
  2. Access a wikipedia page: (how about Lagrange multipliers)
  3. Run the bookmarked script
Remember to show support for Wikipedia!

2012年1月5日木曜日

ウェアラブルにおいセンサを作ってみよう その1

概要

こんなことがしてみたい:
  • 一日のいつなにをしたあと口臭が増える?
  • 足について同上
  • 世界臭いマップを作ってみたい(パリとNYは臭い。ロンドンはそうでもない。)
  • 大のほうの臭いを定量測定して健康管理


材料

  • Arduino Uno
  • においセンサ TGS2450
  • 自作SD+RTCシールド

他:


てなわけで・・・

作ってみた。(図1)
1段目はArduino Uno
2段目はSDカードとRTCの自作シールド。RTCには電気二重層コンデンサがバックアップ電源として繋がっているので、電池の抜き差しにも強い。
3段目は温度センサ(左後方でグチャグチャしてるケーブル)とにおいセンサ

3段構造になってしまったが、スマホ用ポーチには入った(図2)。
ピカソで売ってた「ファンシーポーチ」に入るんだからきっと着こなせる。

1秒に一回ぐらい測定して、時刻情報と共にSDカードに保存するように実装。
出す値は0から1で、高い程臭い。


図1 Arduino+SD/RTCシールド+においセンサ(右下に伸びてるの)
図2 決してデカすぎることはない。と思う。


測ってみよう

実験1 部屋 vs キムチ

キムチの入ってた容器を嗅がせたら0.60に上昇

通常時は0.35

部屋の通常時: 0.35
キムチが入ってた容器を水洗いしたものにかかげる: 0.60

  • 水洗いしたのにキムチはムチャクチャ臭いね。
  • キムチ嗅がせた後、5分間くらい0.60から値が下がらなくなり、焦った。なにか付着するのだろうか。
  • なお、外に数分掲げても0.31だったので、家が臭い等という事は無い。
  • ちなみに私の息は0.43だった。さっきキムチ食ったからだね。うん。

[編集 12/01/05 01:25]
屁を測ってみたら0.74になった。
キムチ臭いとか言ってる場合じゃなかった。


次やること

  • GPS持って臭そうなところに散歩にでかけてみよう(動物園とか)
  • 毎日の大便臭の記録取って見る

ken@臭いのは今日だけ 12/01/05





2011年12月25日日曜日

Arduinoでミリ秒まで出力出来るRTC8564のライブラリ

秋月電子のRTCモジュール(RTC-8564NB搭載のこいつ)は最小で秒単位の出力しか行えない。
でもセンサ情報の記録(特に1Hz以上で)するんだったら、ミリ秒管理も必要だよね?
秒毎のinterruptとmillis()を組み合わせて、ミリ秒管理も可能なRTCライブラリを作ろう。


まずは・・・

ベースとして以下のライブラリを拝借させてもらう:


このライブラリはbegin()を呼ぶだけでRTCの初期化を全部やってくれて、available()を呼ぶと最新の時刻を取得、そしてyear(), month(), etcで年,月,...をそれぞれ取得出来る。
これを改造して、ms()でミリ秒も取得できるようにしよう。
ついでに、available()という命名が気に入らないので、updateTime()に変更。


秒毎の頭のmillis()を覚える

基本的な考え方は:

  • 1秒に一回、秒が変わったタイミングをinterruptで受け取って、現在の秒の頭時点でのmillis()(=システム起動後の経過時間)を覚える(lastSecUpdateMS)
  • 時刻取得を要求(updateTime())されたら、現在のmillis()からlastSecUpdateMSを引いて、ミリ秒を計算する。


まずはヘッダ:
class RTC8564ms
{
public:
 //time (in millis()) of the start of current second
 unsigned long lastSecUpdateMS; 
 uint8_t data[7];
 int _ms;
 
 RTC8564ms(int); 
 void updateTime();
 void setTime(long years, uint8_t months, uint8_t weekdays, uint8_t days, uint8_t hours, uint8_t minutes, uint8_t seconds);

 int ms();
 uint8_t seconds();
 uint8_t minutes();
 uint8_t hours();
 uint8_t days();
 uint8_t weekdays();
 uint8_t months();
 long years();
 bool century();
 String toString();
};

メンバ変数の説明:

  • lastSecUpdateMSに、現在の秒の頭millis()を格納
  • data[]に、最後にupdateTimeを呼んだ時刻のyears〜secondsを格納
  • _msに、最後にupdateTimeを呼んだ時刻のミリ秒を格納

メンバ関数の説明:

  • ms(): _msを返す
  • seconds()〜century(): そのまんま
  • toString(): フレンドリーな文字列を返す


次に、コンストラクタ:

RTC8564ms::RTC8564ms(int interruptPin)
{
 currentRTC=this;
 lastSecUpdateMS =millis();
 _ms=0;
 
 //make sure we're in input mode
 pinMode(interruptPin,INPUT);
 //attach input for input handler
 attachInterrupt(interruptPin-2, interruptHandler,FALLING);
 
 for(int i=0;i<7;i++)
  data[i]=0;
 
 Wire.begin();
 
 Wire.beginTransmission(RTC8564_SLAVE_ADRS);
 Wire.write((byte)0x01);  //Control2 address
 Wire.write((byte)0x11);  // TI/TP:1(繰り返し割り込みモード),TIE:1(定周期割り込み時、Lレベルの割り込み信号を発生させる)
 Wire.endTransmission();
 
 Wire.beginTransmission(RTC8564_SLAVE_ADRS);
 Wire.write((byte)0x0E);  // Timer Control address
 Wire.write((byte)0x82); //countdown period to 1Hz
 Wire.write((byte)0x01); //interrupt every second
 Wire.endTransmission();
 
}
interruptを受け取るピン番号を引数に取る。Arduino Unoでは2か3のはず。
こちらを参考に、RTCモジュールのピン3をD2かD3に繋げる。

3行目: currentRTCに自分を覚えさせる。interruptのハンドラ(interruptHandler())はグローバル関数しか渡せないので、処理の対象をcurrentRTCで覚える。だったらそもそもsingletonとして設計しろって話だが、そのうち配列で覚えるよ。
7-9行目: 渡されたinterruptピンを念のためINPUTモードにし、interruptHandler(後述)を電圧降下時のinterruptとして登録する。
以降: 1秒に一回interruptが呼ばれるように設定

次は、interruptのハンドラ:
void interruptHandler(){
 //record millis() of current second.000
 currentRTC-> lastSecUpdateMS =millis(); 
}

これが呼ばれたということは秒が変わったということなので、currentRTCで登録されたオブジェクトのlastSecUpdateMSに今のmillis()を格納する。

次に、RTCの最新時刻を取得するupdateTime() (旧available())

void RTC8564ms::updateTime(){
 Wire.beginTransmission(RTC8564_SLAVE_ADRS);
 Wire.write((byte)0x02); // write reg addr 02
 Wire.endTransmission();
 
 Wire.requestFrom(RTC8564_SLAVE_ADRS, 7);
 for(int i=0; i<7; i++){
  if(Wire.available()){
   currentRTC->data[i] = Wire.read();
  }
 }
 long current=millis();
 if(current<lastSecUpdateMS)
  _ms= current;
 else
  _ms=millis()-lastSecUpdateMS;
}
↑prettifyでコード内に「<」があるとバグるのがとてもウザい。なんとかならんのですかね?

RTCから年〜秒情報を読み込む。
更に、今のmillis()から、現在秒の頭で記憶したmillis()を引いて、差分を現在時刻のミリ秒として格納。
millis()は数ヶ月?ぐらいでオーバーフローするので、オーバーフロー時は適当に処理する。誤差の範囲だ誤差の。

ms(), years(), などはそのまんまの実装。
RTCに時刻を設定するための関数setTimeも、大して難しいことはない。詳しくは下記添付参照。


ライブラリ


RTC8564ms [zip]
以上ファイルをダウンロードして、Arduinoのlibrariesフォルダに置く。



実行してみる


projects: Biotope: Arduinoとリアルタイムクロック -1: 定周期タイマー割り込み & スリープで公開されている回路図に基づいて組んで、以下のサンプルコードを動かしてみる:
#include <Wire.h>
#include <RTC8564ms.h>

RTC8564ms *rtc;

void setup(){
  Serial.begin(9600);
  
  rtc=new RTC8564ms(2); //initialize using interrupt pin 2
  
  rtc->setTime(2011,12,3,25,10,30,20);
  
}

void loop(){
  //update rtc
  rtc->updateTime();
  
  //print!
  Serial.println(rtc->toString());
    
  delay(10);
}

実行;
RTC8642ms実行結果

ちゃんと動いてるね。



まとめ
  • millis()を別管理してRTSと同じ簡便さでミリ秒も楽々管理出来るライブラリを作った
  • interrupt中はmillisは止まるので、正直そこまで正確というわけではない。同じ秒のindexを管理するよりはマシってレベルだろうきっと。
  • RTCへのクエリーはどれぐらいのスピードで回せるんだろう?