2013年12月8日

New Style Collection / かごしまITフェスタ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

告知しますと言いながら事後報告になってしまいましたが... 鹿児島市主催のかごしまITフェスタにて色々やったのでご報告。
#ファッションショーなのにダサい格好で登壇しているっていうツッコミは勘弁;;

今回は、鹿児島県内の女子大生によるファッションショー企画団体Lilyとコラボして、音と光のファッションショーというものに協力させていただきました。

モデルが何らかの決めポーズ的なものをすると、それに合わせて効果音が流れるというものを製作。
技術的には、Arduino Fioと加速度センサ(ADXL345)、XBee Pro(ZigBee)で体(主に腕)のモーションを取得して送信&PCで受信、Processingで処理してモーション毎に音を鳴らすという仕組み。

コードに関しては色々と突貫で作ってる部分が多いので、時間が出来たら少し書き直して気が向いたら公開しようかなぁ...っと思っています。

Web系が大忙しで電子工作から離れつつある今日この頃...
そろそろニコ技にも進出したいところなので、ネタ探し中です。

2013年11月3日

Raspberry Piで秋月の無接点プッシュスイッチを使う

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Web系のお仕事が山積みで、数ヶ月間更新出来ていませんでした;;
現在もガッツリ電子工作系に打ち込む時間が無いので、今回はベーシックな小ネタということで。


秋月電子で売ってる「無接点プッシュスイッチ(コード付)」を使う。
"機械的接点がない光学式の無接点プッシュスイッチです。デジタル出力型ICセンサ内蔵でドライブ回路に直結可能。" という説明の通り、ICが載ってるので出てる線はVCC、GND、OUTの3本です。
添付されていた参考資料によると、スイッチを押すとOUTがGNDに落ちるようになっていたので配線は以下。


これに加えてLEDを1個追加したので、図には入れてませんが、GPIO 17(pin11)から1kΩの抵抗を介してLED、GND(pin6)というのを入れています。

プログラム(Python with RPi.GPIO)はこんな感じで。
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
GPIO.setup(4,GPIO.IN)
GPIO.setup(17,GPIO.OUT)

while True:
 if (GPIO.input(4)):
  GPIO.output(17,GPIO.LOW)
 else:
  GPIO.output(17,GPIO.HIGH)
ifとelseの中身を変えてあげれば何にでも使えますね。

もし、ボタンを押したらコマンドを叩く様にしたい場合は、
import RPi.GPIO as GPIO
import subprocess

GPIO.setmode(GPIO.BCM)
GPIO.setup(4,GPIO.IN)
GPIO.setup(17,GPIO.OUT)

while True:
 if (not GPIO.input(4)):
  subprocess.call("shutdown -h now", shell=True)
みたいな感じにしてあげれば良いかも。
#このままだとボタンを押していると連続して叩いてしまうので、一回だけ実行するようにする必要ありです。

追記:pushCheck = Falseとか定義して、以下みたいな感じにすればOK
while True:
 if (not GPIO.input(4) and not pushCheck):
  pushCheck = True
  print "Push!"
  GPIO.output(17,GPIO.HIGH)
 if (GPIO.input(4) and pushCheck):
  pushCheck = False
  GPIO.output(17,GPIO.LOW)

話は変わりますが、12/1に割と大きなネタがあるので、時期が近づいたら改めて告知します。

2013年6月30日

PICでBluetooth SPP - UARTコンバータ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

BluetoothのSPPが使えるモジュール、技適マーク付きの物を買うと5000円以上したりします。
これを市販のUSBドングル型Bluetoothモジュールを使ってなんとかしようというネタ。

使うのはPIC24FJ64GB002というUSB OTGが使えるマイコンで、今のところ買えるのはRSやDigiKeyを除いて共立電子のみ。


回路はこんな感じでOK
USBコネクタ部分の配線がよく分からない順番になってますが、実際は
1 - VCC
2 - D- (図の茶色線)
3 - D+ (図の黄色線)
4 - GND
という感じでAメスコネクタと配線してください。

電圧は3.3V。
PicKit3から電源供給する際はプロジェクトのプロパティから電源供給をONに。(MPLAB X)
Arduinoのシールド化する際は3.3Vから引きます。
(本当はあんまり良くないけど)RX/TXは直でArduinoと接続しても問題なし。

実際に使う場合は、右上のFTDI breakooutが繋がってるRX、TX、GNDをUART通信したいデバイスに接続します。


Arduinoと接続してAndroidとSPPで通信している様子。
ここでは、ONと送るとArduinoの13番pinのLEDが点灯、OFFと送ると消灯というスケッチをArudinoに書き込んでいます。

PIC側のプログラムは、hrdakinori氏のPIC24F_btstackをベースに、UARTからの受信をBuetooth SPPに渡すコードを追加すればOKです。

具体的にはこんな感じで、まずbt_spp.cのbt_spp_recive_callbackを以下の様な感じに。

int bt_spp_recive_callback(uint16_t channel, uint8_t *packet, uint16_t size)
{
 packet[size] = 0;
        
        //SPP to UART
        UART2PrintString(packet);
        
 return 0;
}

更に、main.cのsw_processを以下の様な感じに。

static int sw_process(struct data_source *ds)
{
    if(IFS1bits.U2RXIF == 1){
 char text[100]={0};
        int i = 0;
        char c = 0;

        while(1){
            while(U2STAbits.URXDA) {
                c = U2RXREG;
                text[i] = c;
                i++;
            }
            if (c == '\n') break;
            else if(c == 0) break; // if UART no Connect, break while(1)
        }
        text[i] = 0; //

        //UART2PrintString(text); //debug and callback

        //Bluetooth Send
        bt_spp_send((uint8_t*)text,strlen(text));

        IFS1bits.U2RXIF = 0;

 if ( U2STAbits.OERR) {
  U2STAbits.OERR = 0;
 }
    }
 return 0;
}

あと、コンパイル時にundefined reference to packet_handlerが出るので、btstackのhci.c内のstatic void packet_handlerをvoid packet_handlerに変更します。

PICとのシリアル通信は57600bpsで、Bluetooth側は特に制約は無い(はず)。


こんな感じでSwitch Scienceさんのバニラシールドに載せてしまえば、Arduino UNOやMEGAのシールドとして普通に使えます。

2013年5月19日

Arduino+74HC595+LED16個でPOV

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Arduinoとシフトレジスタ74HC595でLED16個を制御してPOVを作ろうという今更感漂うネタ。
実は4月末に作っていたものですが、なかなか時間がとれず今頃ブログに投稿です...


配線はこんな感じ。 #LED16個への配線で非常に見づらくなっております...


シフトレジスタ1個で8個のLEDが制御可能、2段にすると16個、3段で24個、4段で32個...と言う具合に(電源容量の限り)8の倍数ずつ増やすことができます。

プログラムは、Arduinoのチュートリアルも参考にしながら下記のような感じで。

int latchPin = 8;
int clockPin = 12;
int dataPin = 11;

word data;
word dataArray[][10] = {
  {
    0b0000000000000000,
    0b0000001111110000,
    0b0000110000001100,
    0b0001000000000010,
    0b0001000000000010,
    0b0001000000000010,
    0b0001000000000010,
    0b0000110000001100,
    0b0000001111110000,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0000000000000000,
    0b0000000000000000,
    0b0001000000000100,
    0b0001000000000010,
    0b0001111111111110,
    0b0001000000000000,
    0b0001000000000000,
    0b0000000000000000,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0001100000000100,
    0b0001010000000010,
    0b0001001000000010,
    0b0001000100000010,
    0b0001000010000010,
    0b0001000001000100,
    0b0001000000111000,
    0b0000000000000000,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0000100000000100,
    0b0001000001000010,
    0b0001000001000010,
    0b0001000001000010,
    0b0001000001000010,
    0b0000100010100010,
    0b0000011100011100,
    0b0000000000000000,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0000001100000000,
    0b0000001011000000,
    0b0000001000100000,
    0b0000001000011000,
    0b0000001000000100,
    0b0001111111111110,
    0b0000001000000000,
    0b0000001000000000,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0000100001111110,
    0b0001000001000010,
    0b0001000001000010,
    0b0001000001000010,
    0b0001000010000010,
    0b0000100010000010,
    0b0000011100000010,
    0b0000000000000000,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0000011111110000,
    0b0000100010001000,
    0b0001000001000100,
    0b0001000001000010,
    0b0001000001000010,
    0b0001000001000010,
    0b0000100010000010,
    0b0000011100000100,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0000000000000010,
    0b0001100000000010,
    0b0000011000000010,
    0b0000000110000010,
    0b0000000001000010,
    0b0000000000110010,
    0b0000000000001010,
    0b0000000000000110,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0000011100000000,
    0b0000100010111100,
    0b0001000001000010,
    0b0001000001000010,
    0b0001000001000010,
    0b0001000001000010,
    0b0000100010111100,
    0b0000011100000000,
    0b0000000000000000
  }
  ,
  {
    0b0000000000000000,
    0b0000100000111000,
    0b0001000001000100,
    0b0001000010000010,
    0b0001000010000010,
    0b0001000010000010,
    0b0000100010000010,
    0b0000010001000100,
    0b0000001111111000,
    0b0000000000000000
  }
};

void setup() {
  pinMode(latchPin, OUTPUT);
}

void loop() {
  for (int i = 0; i <=9; i++){
    for (int j = 0; j <= 9; j++) {
      data = dataArray[i][j];
      digitalWrite(latchPin, 0);
      shiftOut(dataPin, clockPin, data);
      digitalWrite(latchPin, 1);
      delay(3);
    }
  }
}

void shiftOut(int myDataPin, int myClockPin, word myDataOut) {

  int i=0;
  int pinState;
  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, OUTPUT);

  digitalWrite(myDataPin, 0);
  digitalWrite(myClockPin, 0);

  for (i=16; i>=0; i--)  {
    digitalWrite(myClockPin, 0);
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else { 
      pinState= 0;
    }
    digitalWrite(myDataPin, pinState);
    digitalWrite(myClockPin, 1);
    digitalWrite(myDataPin, 0);
  }
  digitalWrite(myClockPin, 0);
}
これで、0123456789という数字が連続して表示されるはず。


dataArrayの中身を書き換えることで、数字以外にもアルファベットや漢字、ひらがな、記号などを表示させることができます。


「Talkspace」と表示させた例 (青い点はeneloopモバイルブースターのLEDランプ)

最終目標は自転車のホイールへの搭載なので、次は回転に同期させる部分を作っていきます。

2013年4月16日

フレキシブル太陽電池 TX3-25

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

少し前にSwitch ScienceでSunModというテレビなどのリモコンを太陽電池式に改造するキットが販売開始されたのですが、これに使われている(と思われる)太陽電池を偶然共立エレショップで見つけて買ってしまった訳です。


ご覧の通り非常にフレキシブルで薄いので、円筒形の物の表面に貼り付けたりするのも問題なさそう。(日の当たり具合とかは取りあえず無視して)


さっそく両極にリード線を付けてチェック。日中の屋内でも1.7Vくらいの出力が得られる。


少し曇った日の正午過ぎに西向きの窓の網戸越しに置いて1.9Vくらい。
ちなみに定格は「開放電圧/短絡電流:4.1V/40mA 操作電圧/電流:3V/25mA」だそうで、日光のガシガシ当たるところでは普通に3V出るっぽい。

追記:直射日光を当てると4V出ました。

SunModの様に直列接続のニッケル水素電池2本に並列に接続する形で蓄電して、何かに電源供給するという使い方が良さそうですが、実際に充電できる&どのくらい充電できるのかは謎...
#そのうち検証してみるつもり

2013年3月31日

ミニブレッドボードで遊ぶ (温湿度計)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

秋月電子にて1個150円(2個以上買うと1個あたり150円)で売ってあるミニブレッドボードBB-601
これにどれだけ詰め込めるかという遊びを思いついたので、早速何か簡単なものを作ってみました。

ATmega328P-PU、温度センサ(LM61CIZ)、湿度センサ(HIH4030)、極小キャラクタ液晶、セラロック(16MHz)、抵抗(10kΩ)、コンデンサ(0.1µF)という構成。
#流石に電源まで詰めるのは無理


この状態でUSARTとSPIは空いているので、更に拡張や連携も可能だったり。
温度・湿度センサもデジタル(I2CやSPI)接続の物を利用すれば、もう少し他の物を接続できる。


LCD裏はこんな感じ。
買ってきてすぐは1mmピッチのFFCが付いていますが、何だかんだで扱いにくいので、細めで少し硬いビニル絶縁電線に付け替えました。(自立可能)

このサイズで何か面白いものが出来れば掲載しようと思っているものの、如何せんネタが無いのである(~_~;)

追記:サイズ比較にiPhone4Sの上に載せて1枚。


2013年3月22日

ArduinoISPでUSBasp-Bをアップデート

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

aitendoでUSBasp-Bを買ったものの、Arduino IDEからバージョン古いと怒られる&書き込み速度が遅いのでバージョンアップを試みた時のメモ。

ArduinoISPでUSBasp-Bをアップデートする試みは既にこちらなどで挑戦されているようですが、今のところ成功したという記事は見かけなかったのでチャレンジしてみることに。

1. ArduinoにArduinoISPを焼く
ファイル→スケッチの例→ArduinoISPを選択し、あとは普通にいつもの通り書き込む。

2. Auto Resetの無効化
ArduinoのAuto Resetを無効化するために、ArduinoのRESETとGND間にコンデンサを入れる。(0.1µFとか1µFでOK)
ちなみに、110Ω程度の抵抗をRESET-GND間に入れるというduemilanoveで使える技はUNOでは使えませんでした。

3. ArduinoとUSBasp-Bを接続
USBasp-BのJP3とJP4(3つのジャンパのうち真ん中以外の2つ)をONにしてファームウェア書き換えモードにする。
ArduinoとUSBasp-Bの接続はストレート(ArduinoのMOSIはUSBasp-BのMOSIに、MISOはMISOに接続)
ここで、USBasp-BのRESETはArduinoの10番pinに接続する。
ちなみに、この記事の一番上の写真は間違った接続をしていて、正解はこんな感じ。


ArduinoのICSPコネクタ5番pinはRESET(Arduinoの1番pin)に繋がっているため、USBasp-B側のRESETが(Auto Reset無効化の影響で)うまく効かず、

avrdude: stk500_recv(): programmer is not responding

を吐く原因となります。

4. 書き込み
USBaspのウェブサイトから最新版のファームウェアをダウンロード、展開し、ターミナルから下記コマンドで書き込み。

avrdude -P/dev/tty.usbmodemfd121 -b 19200 -c avrisp -p m8 -U flash:w:usbasp.atmega8.2011-05-28.hex:i

「/dev/tty.usbmodemfd121」は各自の環境に合わせて変更してください。
「usbasp.atmega8.2011-05-28.hex」も最新バージョンが出たらそれに合わせて適宜。

うまく書き込めたら下記の様な結果が返ってくると思います。
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.06s

avrdude: Device signature = 0x1e9307
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "usbasp.atmega8.2011-05-28.hex"
avrdude: writing flash (4700 bytes):

Writing | ################################################## | 100% 5.73s

avrdude: 4700 bytes of flash written
avrdude: verifying flash memory against usbasp.atmega8.2011-05-28.hex:
avrdude: load data flash data from input file usbasp.atmega8.2011-05-28.hex:
avrdude: input file usbasp.atmega8.2011-05-28.hex contains 4700 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 5.41s

avrdude: verifying ...
avrdude: 4700 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

最後にヒューズビット(hfuse)を書き換える。

avrdude -P/dev/tty.usbmodemfd121 -b 19200 -c avrisp -p m8  -U hfuse:w:0xC9:m

これも、うまくいけば下記の様な結果が返ってくる。
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.07s

avrdude: Device signature = 0x1e9307
avrdude: reading input file "0xC9"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.07s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xC9:
avrdude: load data hfuse data from input file 0xC9:
avrdude: input file 0xC9 contains 1 bytes
avrdude: reading on-chip hfuse data:

Reading | ################################################## | 100% 0.02s

avrdude: verifying ...
avrdude: 1 bytes of hfuse verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

以上でバージョンアップ終わり。

Auto Resetの影響を受ける可能性を思いつくまで30分、ICSPの5番pinがRESETに接続されているのを思い出すまで30分...
0x14歳にして脳が衰退し始めていることを思い知らされた1時間でしたorz

2013年2月11日

iPodをArduinoでコントロール

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

コントロールと言っても音楽やメディア(YouTube、ストリーミング等)の再生停止、送り戻し、音量の調節くらいですが、今更ながらDockコネクタ経由でやってみたり。
#時代はLightningとか言わない
一応、iOSデバイス(iPhone/iPad)も制御できますが、あくまでもメディア(音楽・動画)のコントロールのみなのでタイトルを「iPod」としました。

使用したのはArduino Pro mini 3.3V、スイッチ、抵抗(1kΩ*3、1MΩ*1)、Dockコネクタのbreakoutボードの4つ。
5V系を使う場合はTXからの出力を抵抗分圧等で3.3VにレベルシフトしてあげればOK。
(ただし、Arduinoへの電源供給は別途しなければならない)


とりあえず、再生停止、曲送り、曲戻しの3ボタンを用意。
Dockコネクタとの接続はこちら(http://pinouts.ru/CellularPhones-A-N/iPhone_connector_pinout.shtml)を参考に、ArduinoのTXをDockコネクタの13ピン、VCCを18ピン、GNDを11ピンに。
Dockコネクタの21ピン(Accessory Indicator)は1MΩ経由でGNDに接続しました。(500kΩでも問題なし)

iPod(iPhone/iPad含む)のシリアル通信に関しては既に調べ尽くされているので、その情報を参考にしました。
https://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/s2007/awr8_asl45/awr8_asl45/index.html
http://web.student.tuwien.ac.at/~e0026607/ipod_remote/ipod_ap.html

シリアル通信には複数のモードがあり、最も簡単な制御モード(iPodへの一方行通信)がmode2、曲名やアルバム名なども取得できる制御モード(iPodとの双方向通信)がmode4となっている模様。
接続直後はmode0となっているので、そこにmode2への切り替えコードを叩いてあげるとmode2の制御コマンドを受け付けるようになってくれます。
と言うわけで、プログラムは上記ページを参考にしながら以下の様な感じで。
#include <Bounce.h>

#define BUTTON1_PIN 3
#define BUTTON2_PIN 4
#define BUTTON3_PIN 5

boolean state[3];

Bounce button1 = Bounce( BUTTON1_PIN,20 );
Bounce button2 = Bounce( BUTTON2_PIN,20 );
Bounce button3 = Bounce( BUTTON3_PIN,20 );

byte mode2[] =      {
  0xFF, 0x55, 0x03, 0x00, 0x01, 0x02, 0xFA};
byte playStop[] =   {
  0xFF, 0x55, 0x03, 0x02, 0x00, 0x01, 0xFA};
byte prevTrack[] =   {
  0xFF, 0x55, 0x03, 0x02, 0x00, 0x10, 0xEB};
byte nextTrack[] =   {
  0xFF, 0x55, 0x03, 0x02, 0x00, 0x08, 0xF3};
byte buttonReleased[] = {
  0xFF, 0x55, 0x03, 0x02, 0x00, 0x00, 0xFB};

void setup() {
  pinMode(BUTTON1_PIN, INPUT);
  pinMode(BUTTON2_PIN, INPUT);
  pinMode(BUTTON3_PIN, INPUT);
  Serial.begin(19200);
  for (int i = 0; i < 7; i++) {
    Serial.write(mode2[i]);
  }
}

void loop() {
  button1.update();
  if ((button1.read() == HIGH)&&(state[0] == 0)) {
    for (int i = 0; i < 7; i++) {
      Serial.write(playStop[i]);
    }
    state[0] = 1;
  }
  if ((button1.read() == LOW)&&(state[0] == 1)) {
    for (int i = 0; i < 7; i++) {
      Serial.write(buttonReleased[i]);
    }
    state[0] = 0;
  }
  button2.update();
  if ((button2.read() == HIGH)&&(state[1] == 0)) {
    for (int i = 0; i < 7; i++) {
      Serial.write(prevTrack[i]);
    }
    state[1] = 1;
  }
  if ((button2.read() == LOW)&&(state[1] == 1)) {
    for (int i = 0; i < 7; i++) {
      Serial.write(buttonReleased[i]);
    }
    state[1] = 0;
  }
  button3.update();
  if ((button3.read() == HIGH)&&(state[2] == 0)) {
    for (int i = 0; i < 7; i++) {
      Serial.write(nextTrack[i]);
    }
    state[2] = 1;
  }
  if ((button3.read() == LOW)&&(state[2] == 1)) {
    for (int i = 0; i < 7; i++) {
      Serial.write(buttonReleased[i]);
    }
    state[2] = 0;
  }
}
Bounceライブラリを使うとなんか冗長的な感じになってしまうのがアレですが、とりあえず仕組みは理解できたので今回はこれで良しとします。

これで何を作りたいのかと言うと、iOSデバイス上での音楽再生を停止させるスリープタイマーの外部インターフェース版。
普通はiOSデバイス単体でスリープタイマーを設定出来るようになっていますが、そういう設定が面倒くさい人(父)の為に、ボタンを「物理的に」何回か押せば簡単にタイマーが設定出来る装置を作ろうかと。

色々と資料を見ているとmode4も面白そうなので、次はmode4で取得した情報をLCDに表示させたりしてみようと思っているところ。

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 UNOEthernet 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くらいの誤差でつぶやきます。

使用したのは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

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

お家のトイレからプライバシーが吹き飛ぶ日がやってきました。
事の発端は以下のツイート
このツイートに触発されて、Arduino + Ethernet Shield + 赤外線リモコン受信モジュールで作ってしまったのが上記写真。

実際のツイートはこんな感じ。(一部)
という具合。

我が家の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を宣言して、
   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 : 実際のツイートを開発用アカウントからメインアカウントのツイートに差し替えしました。