Pages
▼
2013年1月24日
Arduino DUEが届いた
供給が追いつかず、どこも品切れ状態が続いているArduino DUE、やっと購入でき、本日届きました。
右の写真は、奥から順にArduino UNO、Arduino MEGA 2560、Arduino DUEの3兄弟(?)
DUEを購入するついでにWireless Proto Shieldも購入。
新しいShieldは、パターンを追ってみるとICSP、VCC&GNDピンの両方から電源を引いてる様なので変な3段重ねをしなくてもよさそう。
2013年1月22日
ウォシュレット実況botの無線化
ウォシュレット実況bot と トイレットペーパー使用量bot を合体して無線化しました。
今まではトイレから直接送信(有線LAN)していましたが、隣の部屋からLANケーブルを引くのも滑稽なのでXBeeで無線化することに。
上の写真はトイレに設置する送信側で、Pro mini 3.3V、スイッチサイエンスのXBeeピッチ変換基板、XBee(S2/ZB)、赤外線受信モジュール、ロータリエンコーダ、Li-poで構成。
下の写真は別室に設置する受信側で、Arduino UNO、Ethernet Shield、XBee Shieldで構成。
古いXBee Shieldは電源がICSPから取ってあるらしく、Ethernet Shieldと3段重ねにするとXBee Shieldに電源供給されません。(Ethernet ShieldにICSPピンヘッダが無い)
この様な細工で電源供給して、ピンのカサ増しの為にピンヘッダを間に入れています。
プログラムは、ウォシュレットbotとトイレットペーパーbotを合体した後、赤外線受信&エンコーダのカウント&シリアル送信部をトイレ設置機に、シリアル受信&Twitterへの投稿部を別室設置機にという具合です。
ちなみに、XBee(S2/ZB)とピッチ変換基板とXBee USBアダプタは、電子工作コンテスト2012の はんだづけカフェ賞 でスイッチサイエンスさんから頂いた物です。
#初めてのXBeeがこういう形で使うことになるとは...
2013年1月19日
ふぁぼマウスを作ってみた
Sun Microsystems製の3ボタンマウス(Model Type 5)が廃棄されていたので、ボタン部分を活かしてtwitter(web版)を快適に閲覧する為の装置を作ってみました。
※タイトルにふぁぼ"マウス"とありますが、マウス本来の機能(カーソル操作)は出来ません。
このマウス、専用マウスパッドが必要な光学式&接続がMini-DIN3ピンなのでDOS機には使えず、そのままでは歴史的資料価値しかありません。
バラすとこんな感じで、ボタン部にはオムロン製のマイクロスイッチが3つ使用されている。
マイコン・光学周りは用途が微妙だったので、マイクロスイッチのみ元の基板から取り外して、マウスの幅を合わせてカットしたCサイズ基板に移植。
こちらを参考にして、ブレッドボード上でV-USBの回路を組んで動作チェック。
3.6Vのツェナーダイオードが手元に無かったのでVfが3.6Vの青色LEDを使用、68Ωの抵抗もなかったので100Ωの抵抗を使用。
MacBook Air、MacBook Pro Retinaでは問題なく認識したので、たぶん大丈夫のはず...
これをマイクロスイッチを移植した基板に実装。(裏面のはんだモリモリは気にしたら負け)
Arduino IDE 1.0以降に対応したV-USBライブラリをこちらからダウンロード、適宜libraryフォルダに投入。
プログラムはこんな感じで。
#include "UsbKeyboard.h"
#define BUTTON_L 12
#define BUTTON_C 11
#define BUTTON_R 10
int lastState = LOW;
int lastStateC = LOW;
unsigned long pushTime = 0;
void setup() {
  pinMode(BUTTON_L, INPUT);
  pinMode(BUTTON_C, INPUT);
  pinMode(BUTTON_R, INPUT);
  TIMSK0 &= !(1<TOIE0);
  cli();
  usbDeviceDisconnect();
  delayMs(250);
  usbDeviceConnect();
  sei();
}
void loop() {
  UsbKeyboard.update();
  
  //中央ボタンの処理
  if( (digitalRead(BUTTON_C) == HIGH) && (lastStateC==LOW) ) {
    lastStateC = HIGH;
    pushTime = millis(); //長押し計測開始
    delay(70);
  } 
  else if( (digitalRead(BUTTON_C) == LOW) && (lastStateC==HIGH) ) {
    if( (millis() - pushTime) > 500 ){ //500ミリ秒以上長押しの場合
      UsbKeyboard.sendKeyStroke(KEY_T); //Tを送信
      delay(200);
      UsbKeyboard.sendKeyStroke(88); //Enterを送信
      lastStateC = LOW;
    }
    else{
      UsbKeyboard.sendKeyStroke(KEY_F); //Fを送信
      delay(70);
      lastStateC = LOW;
    }
  }
  //左ボタンの処理
  else if( (digitalRead(BUTTON_L) == HIGH) && (lastState==LOW) ) {
    delay(30);
    if( (digitalRead(BUTTON_R) == HIGH) ) { //右ボタンも押されている場合
      UsbKeyboard.sendKeyStroke(55); //ピリオドを送信
      lastState = HIGH;
      delay(70);
    }
    else {
      UsbKeyboard.sendKeyStroke(KEY_J); //Jを送信
      lastState = HIGH;
      delay(70);
    }
  } 
  else if( (digitalRead(BUTTON_L) == LOW) && (lastState==HIGH) ) {
    lastState = LOW;
    delay(100);
  }
  //右ボタンの処理
  else if( (digitalRead(BUTTON_R) == HIGH) && (lastState==LOW) ) {
    delay(30);
    if( (digitalRead(BUTTON_L) == HIGH) ) { //左ボタンも押されている場合
      UsbKeyboard.sendKeyStroke(55); //ピリオドを送信
      lastState = HIGH;
      delay(70);
    }
    else {
      UsbKeyboard.sendKeyStroke(KEY_K); //Kを送信
      lastState = HIGH;
      delay(70);
    }
  } 
  else if( (digitalRead(BUTTON_R) == LOW) && (lastState==HIGH) ) {
    lastState = LOW;
    delay(100);
  }
}
void delayMs(unsigned int ms) {
  for( int i=0; i<ms; i++ ) {
    delayMicroseconds(1000);
  }
}
twitterのショートカットと各ボタンが対応しており、左ボタンで下のツイートへ移動(Jキー)、右ボタンで上のツイートへ移動(Kキー)、左右同時押しで更新(ピリオドキー)、真ん中ボタン1回押しでfav(Fキー)、真ん中ボタン長押し(0.5秒)でRT(Tキー)という感じ。
2013年1月12日
トイレットペーパー使用量報告bot
ウォシュレットでスッキリした後はお尻を拭かなければなりません。
という訳で、お家のトイレからプライバシーが吹き飛ぶガジェット第二弾は、Arduino + Ethernet Shield + ロータリエンコーダでトイレットペーパーの使った長さをツイートする装置。(上記写真)
実際のツイートはこんな感じ。
という具合に、長さを±1cmくらいの誤差でつぶやきます。おしりを拭きました(´ε`;)フキフキ ペーパー長さ79cm
— けーいち (@9SQ) 2013年1月20日
使用したのはArduino UNO、Ethernet Shield、ロータリエンコーダ(マウスから入手)の3つ。
故障気味のマウスを分解、マウスのホイール部分を取り出します。
これをArduinoに接続。
写真では試験的にMEGA2560を使ってますが、実際はUNOで事足ります。
ロータリエンコーダはA、B、2つのパルスの位相が1/4周期ずれて出力されるので、このパルスをそれぞれカウントすることで回転方向と回転量を導出することが出来る。
このパルスを検出してカウントするためにattachInterruptを使いますが、このとき外部割り込みに使用できるピンはデジタル2番、3番ピン。
エンコーダは、端子が出ている方の面を手前にして左からA、B、GNDとなっている(メーカによって異なる)ので、AをArduinoのデジタル2番ピン、Bを3番ピンに接続します。
更に、ホイールの表面がトイレットペーパーの表面に当たるように、紙巻器に固定します。
長さの計測開始は約1cmからで、計測終了&ツイートはトイレットペーパーの引き出しが終わってから3秒後(3秒間エンコーダが回転しなかったら終了と見なす)としました。
プログラムは、こちらを参考にしつつ、以下のような感じで。
//Copyright 2012 bildr
//Released under the MIT License - Please reuse change and share
//modified by Kei.Y - Additional portions license is CC0
#include <SPI.h>
#include <Ethernet.h>
#include <Twitter.h>
byte mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //Ethernet ShieldのMACアドレス
byte ip[] = { 192, 168, 1, 123 }; //IPアドレス
Twitter twitter("/* 取得したトークン */");
char msg[256];
int encoderPin1 = 2;
int encoderPin2 = 3;
volatile int lastEncoded = 0;
volatile long encoderValue = 0;
long lastencoderValue = 0;
long encoderValueTemp = 0;
long lastUse = 0;
int stopCount = 0;
int lastMSB = 0;
int lastLSB = 0;
void setup() {
  Ethernet.begin(mac, ip); 
  Serial.begin (9600);
  pinMode(encoderPin1, INPUT); 
  pinMode(encoderPin2, INPUT);
  digitalWrite(encoderPin1, HIGH);
  digitalWrite(encoderPin2, HIGH);
  attachInterrupt(0, updateEncoder, CHANGE); 
  attachInterrupt(1, updateEncoder, CHANGE);
}
void loop(){ 
  if(encoderValue > lastUse && (encoderValue - lastUse)>5){
    if(encoderValue == encoderValueTemp){
      stopCount++;
      if(stopCount > 3 ){
        sprintf(msg,"おしりを拭きました(´ε`;)フキフキ 長さ%dcm",(int)((encoderValue - lastUse)/5.4));
        postTweet();
        lastUse = encoderValue;
        stopCount = 0;
      }
    }
    encoderValueTemp = encoderValue;
  }
  delay(1000);
}
void updateEncoder(){
  int MSB = digitalRead(encoderPin1);
  int LSB = digitalRead(encoderPin2);
  int encoded = (MSB << 1) |LSB;
  int sum  = (lastEncoded << 2) | encoded;
  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
  lastEncoded = encoded;
}
void postTweet(){
  Serial.println("connecting ...");
  if (twitter.post(msg)) {
    int status = twitter.wait();
    if (status == 200) {
      Serial.println("OK.");
    }
    else {
      Serial.print("failed : code ");
      Serial.println(status);
    }
  } 
  else {
    Serial.println("connection failed.");
  }
}
使用用途として、トイレットペーパー使用量を他人と比較したり、無駄づかい防止・節約の為の意識付けなどがありますが、プライバシーも一緒に流れていく事に変わりはありません。追記:ウォシュレット実況botと合体させて無線化しました。
ウォシュレット実況botの無線化
http://eleclog.quitsq.com/2013/01/toilet-tweet-bot-wireless.html
追記 2016.01.03 : 実際のツイートを開発用アカウントからメインアカウントのツイートに差し替えしました。
2013年1月5日
ウォシュレット実況bot
お家のトイレからプライバシーが吹き飛ぶ日がやってきました。
事の発端は以下のツイート
トイレ流すリモコンの信号を受信すればすぐ実現出来そう。大小の区別も…。 QT @mattn_jp: 便器からツィートが流れだすまであと少し / “人間とウェブの未来 - mruby-oauthでmrubyからtwitterを操作” htn.to/sXuVQq
— きくちゃんさん (@kikuchan98) 1月 1, 2013
トイレのリモコン、押された時刻や種別が蓄積されてたらヤだな…
— きくちゃんさん (@kikuchan98) 1月 1, 2013
このツイートに触発されて、Arduino + Ethernet Shield + 赤外線リモコン受信モジュールで作ってしまったのが上記写真。実際のツイートはこんな感じ。(一部)
おしり洗浄中( ´_ゝ`)
— けーいち (@9SQ) 2013年1月20日
という具合。トイレを流しました(小)
— けーいち (@9SQ) 2013年1月20日
我が家のTOTOのウォシュレット、そのリモコンから送信されるコードを赤外線受信モジュールでキャッチし、押されたボタンを判別してツイートします。
使用したのはArduino UNO、Ethernet Shield、赤外線リモコン受信モジュール (PL-IRM2121)の3つ。
配線を簡略化する為に、5番ピンを赤外線モジュールとの通信、6番ピンをGND、7番ピンをVccにしています。
まずは、リモコンが送信しているコードの取得。
6.1ミリ秒のON、2.9ミリ秒のOFFに続いて、0.6ミリ秒、0.5ミリ秒間隔でON、OFFを繰り返し、1.5ミリ秒のOFFが間に何回か入ります。
Excelに起こすとこんな感じ。
左上:流す(小)、左下:流す(大)、右上:おしり、右中:やわらか、右下:ビデ
最初の6.1ms、2.9msはリモコン毎に異なる送信開始の合図リーダ部で、その後がデータ部となっている。
ここで、ON時間を無視してOFF時間だけ抽出し、1.5ミリ秒以上のOFF時間を1、0.5ミリ秒のOFF時間を0とすると
010000000001000000000001011000010110000:大
010000000001000000000001000100010001000:小
010000000001000001011001000000010101100:おしり
010000000001000001011001010100010000100:やわらか
010000000001000001011000100000001101100:ビデ
010000000001000101010101101101001110000:ストップ
という風に表すことができる。
あとはこれを条件分岐し、それぞれにあったツイートをpostさせればOK。
プログラムは、こちらやこちらやこちらを参考にしつつ、以下のような感じで。
#include <SPI.h>
#include <Ethernet.h>
#include <Twitter.h>
#define IR_INPUT 5
byte mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //Ethernet ShieldのMACアドレス
byte ip[] = { 192, 168, 1, 123 }; //IPアドレス
Twitter twitter("/* 取得したトークン */");
char msg[256];
void setup()
{
  Ethernet.begin(mac, ip);   
  Serial.begin(9600);
  pinMode(IR_INPUT,INPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  digitalWrite(6,LOW);
  digitalWrite(7,HIGH);
}
void loop()
{
  unsigned long time = 0;
  int i , cnt;
  char buf[64];
  if (digitalRead(IR_INPUT) == LOW) {
    time = micros();
    while (digitalRead(IR_INPUT) == LOW) ;
    time = micros() - time;
  }
  if (time >= 6100) {
    i = 0 ;
    while(digitalRead(IR_INPUT) == HIGH) ;
    while (1) {
      while(digitalRead(IR_INPUT) == LOW) ;
      time = micros() ;
      cnt = 0 ;
      while(digitalRead(IR_INPUT) == HIGH) {
        delayMicroseconds(10) ;
        cnt++ ;
        if (cnt >= 1200) break ;
      }
      time = micros() - time ;
      if (time >= 10000) break ;
      if (time >= 1500)  buf[i] = (char)0x31 ;
      else               buf[i] = (char)0x30 ;
      i++ ;
    }
    if (i != 0) {
      buf[i] = 0 ;
      checkCode(buf) ;
    }
  }
}
void checkCode(String code)
{
  code = code.substring(15,37);
  Serial.println(code);
  if (code.equals("0000000010110000101100")){
    strcpy(msg,"トイレを流しました(大)");
    postTweet();
  }
  else if (code.equals("0000000010001000100010")){
    strcpy(msg,"トイレを流しました(小)");
    postTweet();
  }
  else if (code.equals("0010110010000000101011")){
    strcpy(msg,"おしり洗浄中( ´_ゝ`)");
    postTweet();
  }
  else if (code.equals("0010110010101000100001")){
    strcpy(msg,"おしりやわらか洗浄中(*´ω`*)");
    postTweet();
  }
  else if (code.equals("1010101011011010011100")){
    strcpy(msg,"洗浄ストップ(`・ω・´)");
    postTweet();
  }
  delay(100);
}
void postTweet()
{
  Serial.println("connecting ...");
  if (twitter.post(msg)) {
    int status = twitter.wait();
    if (status == 200) {
      Serial.println("OK.");
    } 
    else {
      Serial.print("failed : code ");
      Serial.println(status);
    }
  } 
  else {
    Serial.println("connection failed.");
  }
}
使用用途として、お通じの公開追記:噴射時間もツイートするようにしてみた。
unsigned long washTimeを宣言して、洗浄ストップ(`・ω・´) 噴射時間9秒
— けーいち (@9SQ) 2013年1月20日
   else if (code.equals("0010110010000000101011")){
    washTime = millis();
    strcpy(msg,"おしり洗浄中( ´_ゝ`)");
    postTweet();
  }
  else if (code.equals("0010110010101000100001")){
    washTime = millis();
    strcpy(msg,"おしりやわらか洗浄中(*´ω`*)");
    postTweet();
  }
  else if (code.equals("1010101011011010011100")){
    washTime = millis() - washTime;
    sprintf(msg,"洗浄ストップ(`・ω・´) 噴射時間%d秒",(int)(washTime/1000));
    postTweet();
  }
こんな感じで。追記2:トイレットペーパーの使用量もツイートするようにしてみた。
トイレットペーパー使用量報告bot
http://eleclog.quitsq.com/2013/01/toilet-paper-bot.html
追記3:トイレットペーパー使用量botと合体させて、無線化しました。
ウォシュレット実況botの無線化
http://eleclog.quitsq.com/2013/01/toilet-tweet-bot-wireless.html
追記 2016.01.03 : 実際のツイートを開発用アカウントからメインアカウントのツイートに差し替えしました。







