天皇陛下万歳。
国旗を掲揚する。
オッサンは生きている。
天皇陛下万歳。
国旗を掲揚する。
免許証とSuicaと住基カードとTポイントカードとVISAと銀行キャッシュカードを全部1枚におさめることは、技術上は可能なのだが、「絶対ダメーーーーーっ!」と絶叫するものすごい人々がいるので、ダメなのだろうなあ。
全部1枚になったほうがよっぽど便利だと思うが、便利と言うものはだいたいは人生の敵だから、まあ、カードがバラバラにあるのは、それはそれでよい。
LEDを強力に光らせることが出来たので、いよいよWebサーバつき多機能リモコンを作成する。卓上などに置いておき、ネットワークにつないで、スマホなどから複数の電化製品を操作できるというものだ。
ここでは、いくつかのテクニックを使った。
一つは、フォームが大きくなってしまい、ハードコーディングするとメモリが足りなくなる。そこで、SDカード内にHTMLを置き、これを読み出すようにした。
同様に、リモコンから読み取った数値データが大きくなって、普通にハードコーディングしたのではメモリが不足する。そこで、「PROGMEM」というキーワードを使って、フラッシュメモリ内にデータを置き、これを読み出すようにした。
ETHERNET SHIELD 2を遺憾なく使う。SDカードを取り付けられるから、そこにHTMLを書き込んでおけばよい。FETをつけたブレッドボードと一緒に、買っておいたSparkfunの基盤に固定する。
SDカード内には、次のようなHTMLを置き、ファイル名を「irform.htm」とする。
<html> <head> <meta name="Editor" content="Notepad.exe"> <meta http-equiv="Content-Type" content="text/html;charset=Shift_JIS"> <title>Webリモコン</title> <basefont size=4> </head> <body bgcolor="#ddffdd"> <center> <h1><b>Webリモコン</b></h1> <table> <tr><td> 作成者 </td> <td align="right">佐藤俊夫</td></tr> <tr><td> 作成日時 </td> <td align="right">27.07.19 (日) 1835</td></tr> </table> </center> <hr> <center> <form method="get" name="irremote"> <table border=1> <tr><th>機器</th><th>ボタン</th></tr> <tr> <td rowspan=4>扇風機</td> <td><input submit type="submit" value="入/風量" name="fan_on"></td> </tr> <tr><td><input submit type="submit" value="タイマー" name="fan_timer"></td></tr> <tr><td><input submit type="submit" value="首振" name="fan_swing"></td></tr> <tr><td><input submit type="submit" value="切" name="fan_off"></td></tr> </tr> <tr> <td rowspan=5>テレビ</td> <td><input submit type="submit" value="入/切" name="tv_on_off"></td> </tr> <tr><td><input submit type="submit" value="音量大" name="tv_volup"></td></tr> <tr><td><input submit type="submit" value="音量小" name="tv_voldown"></td></tr> <tr><td><input submit type="submit" value="チャンネル>" name="tv_chup"></td></tr> <tr><td><input submit type="submit" value="チャンネル<" name="tv_chdown"></td></tr> </table> </form> </center> </body> </html>
スケッチは次のようになる。
// // Web2IRremote.ino // リモコンをウェブで操作する。 // 27.07.25(日) 1930 // 佐藤俊夫 // #include <SPI.h> #include <Ethernet2.h> #include <SD.h> #include <IRremote.h> #include <avr/pgmspace.h> // byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0xF6, 0x74 }; IPAddress ip(192, 168, 1, 129); EthernetServer SERVER(80); EthernetClient CLIENT; IRsend irsend; PROGMEM const unsigned int fan_on[] = {4500,2150, 600,500, 650,500, 600,500, 600,500, 600,1650, 600,1600, 650,1550, 650,500, 600,1600, 650,1600, 600,1600, 650,450, 600,1600, 650,500, 600,500, 650,500, 600,1600, 600,500, 650,500, 600,500, 600,500, 600,500, 600,1650, 600,500, 600,500, 600,500, 650,1600, 600,1600, 600,1650, 600,1600, 600,500, 650,500, 600,0}, fan_timer[] = {4500,2150, 550,550, 550,600, 550,550, 600,500, 550,1700, 500,1700, 550,1650, 550,600, 550,1650, 550,1700, 500,1700, 550,600, 500,1700, 550,550, 550,550, 550,600, 500,1700, 550,600, 500,600, 500,600, 500,600, 550,550, 550,1700, 550,600, 450,600, 550,550, 550,600, 500,1700, 550,1650, 550,1700, 550,1650, 550,600, 500,0}, fan_swing[] = {4450,2200, 600,550, 550,500, 600,550, 550,550, 550,1700, 550,1650, 550,1700, 550,550, 550,1650, 550,1700, 550,1650, 550,550, 600,1650, 550,550, 550,550, 550,550, 600,1650, 550,550, 550,550, 600,550, 550,550, 550,550, 600,1600, 600,550, 550,550, 550,1650, 600,1550, 650,1700, 550,1650, 550,550, 550,550, 600,550, 550,0}, fan_off[] = {4450,2250, 550,550, 600,550, 550,500, 600,550, 550,1650, 600,1650, 550,1650, 600,550, 550,1650, 550,1650, 600,1650, 550,550, 550,1700, 550,550, 550,550, 550,550, 550,1700, 550,550, 550,550, 550,550, 600,550, 550,550, 550,1650, 550,550, 600,550, 550,1650, 600,1650, 550,550, 550,1700, 550,500, 600,550, 550,1650, 600,0}, tv_on_off[] = {3400,1750, 400,500, 350,1350, 400,500, 350,500, 400,500, 350,500, 400,500, 350,450, 400,500, 400,500, 350,500, 400,500, 350,500, 350,1350, 400,500, 400,500, 350,450, 400,500, 400,500, 350,450, 400,500, 400,500, 350,500, 400,1300, 400,500, 400,450, 400,500, 400,500, 350,500, 350,500, 400,450, 400,450, 400,1350, 400,500, 400,1300, 400,1350, 400,1350, 400,1350, 400,450, 400,500, 400,1300, 400,500, 400,1300, 400,1350, 400,1350, 400,1350, 400,500, 350,1350, 400,0}, tv_volup[] = {3400,1750, 400,500, 400,1300, 400,500, 400,500, 350,500, 450,400, 400,500, 350,500, 400,500, 350,500, 350,500, 400,500, 350,500, 400,1300, 400,500, 400,450, 400,500, 350,500, 400,500, 350,500, 400,500, 350,500, 350,500, 400,1300, 450,450, 400,500, 350,500, 400,500, 350,500, 400,450, 400,500, 350,500, 400,500, 350,450, 400,500, 400,500, 350,500, 450,1250, 400,500, 400,500, 350,500, 400,450, 400,500, 350,500, 400,500, 350,1350, 400,450, 400,1350, 400,0}, tv_voldown[] = {3400,1750, 400,500, 350,1350, 400,500, 400,450, 400,500, 350,500, 400,450, 400,500, 400,500, 350,500, 350,500, 400,500, 350,500, 350,1300, 450,500, 400,500, 350,450, 400,500, 450,450, 350,500, 400,450, 400,450, 400,500, 400,1300, 450,500, 350,500, 350,500, 400,500, 350,450, 400,500, 400,500, 350,500, 400,1300, 400,500, 400,450, 400,500, 400,500, 350,1350, 400,500, 350,500, 400,1300, 400,500, 400,500, 350,500, 350,500, 400,1300, 450,500, 350,1350, 400,0}, tv_chup[] = {3400,1750, 400,500, 400,1350, 450,400, 400,450, 400,500, 450,400, 400,450, 400,500, 450,450, 450,400, 450,450, 400,400, 400,500, 450,1250, 450,500, 350,450, 500,400, 450,400, 400,500, 350,500, 450,450, 350,500, 450,450, 450,1250, 400,500, 450,400, 400,500, 400,450, 450,400, 450,450, 350,500, 450,400, 450,450, 350,500, 450,1250, 400,500, 450,1250, 500,1250, 500,400, 450,450, 350,500, 450,450, 350,1350, 450,450, 400,1300, 450,1250, 500,450, 400,1250, 450,0}, tv_chdown[] = {3500,1650, 450,500, 400,1300, 400,500, 450,400, 350,500, 400,450, 500,400, 450,450, 350,450, 400,500, 400,500, 350,500, 400,450, 450,1300, 450,450, 350,450, 450,500, 350,500, 450,400, 450,400, 400,500, 350,500, 450,400, 400,1350, 450,400, 500,400, 450,450, 450,400, 400,500, 350,500, 350,500, 400,500, 350,1350, 450,450, 350,1350, 400,500, 350,1350, 500,1250, 450,450, 450,400, 450,1250, 500,400, 450,1300, 450,450, 400,1300, 450,1250, 450,500, 350,1350, 450,0}; //// void setup() { const int chipSelect = 4; Ethernet.begin(mac, ip); SERVER.begin(); if (!SD.begin(chipSelect)) { return; } } void loop() { // Webサーバの動作 char c; String rstr = ""; CLIENT = SERVER.available(); if (CLIENT) { while (CLIENT.connected()) { if (CLIENT.available()) { c = CLIENT.read(); rstr += c; if(rstr.endsWith("\r\n")){ break; } } } if(rstr.indexOf("fan_on=") >= 0){ unsigned int buf[sizeof(fan_on) / sizeof(*fan_on)]; for(int i = 0; i < sizeof(fan_on) / sizeof(*fan_on); i++){ buf[i] = pgm_read_word(fan_on + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } else if(rstr.indexOf("fan_timer=") >= 0){ unsigned int buf[sizeof(fan_timer) / sizeof(*fan_timer)]; for(int i = 0; i < sizeof(fan_timer) / sizeof(*fan_timer); i++){ buf[i] = pgm_read_word(fan_timer + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } else if(rstr.indexOf("fan_swing=") >= 0){ unsigned int buf[sizeof(fan_swing) / sizeof(*fan_swing)]; for(int i = 0; i < sizeof(fan_swing) / sizeof(*fan_swing); i++){ buf[i] = pgm_read_word(fan_swing + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } else if(rstr.indexOf("fan_off=") >= 0){ unsigned int buf[sizeof(fan_off) / sizeof(*fan_off)]; for(int i = 0; i < sizeof(fan_off) / sizeof(*fan_off); i++){ buf[i] = pgm_read_word(fan_off + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } else if(rstr.indexOf("tv_on_off=") >= 0){ unsigned int buf[sizeof(tv_on_off) / sizeof(*tv_on_off)]; for(int i = 0; i < sizeof(tv_on_off) / sizeof(*tv_on_off); i++){ buf[i] = pgm_read_word(tv_on_off + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } else if(rstr.indexOf("tv_volup=") >= 0){ unsigned int buf[sizeof(tv_volup) / sizeof(*tv_volup)]; for(int i = 0; i < sizeof(tv_volup) / sizeof(*tv_volup); i++){ buf[i] = pgm_read_word(tv_volup + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } else if(rstr.indexOf("tv_voldown=") >= 0){ unsigned int buf[sizeof(tv_voldown) / sizeof(*tv_voldown)]; for(int i = 0; i < sizeof(tv_voldown) / sizeof(*tv_voldown); i++){ buf[i] = pgm_read_word(tv_voldown + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } else if(rstr.indexOf("tv_chup=") >= 0){ unsigned int buf[sizeof(tv_chup) / sizeof(*tv_chup)]; for(int i = 0; i < sizeof(tv_chup) / sizeof(*tv_chup); i++){ buf[i] = pgm_read_word(tv_chup + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } else if(rstr.indexOf("tv_chdown=") >= 0){ unsigned int buf[sizeof(tv_chdown) / sizeof(*tv_chdown)]; for(int i = 0; i < sizeof(tv_chdown) / sizeof(*tv_chdown); i++){ buf[i] = pgm_read_word(tv_chdown + i); } irsend.sendRaw(buf, sizeof(buf) / sizeof(buf[0]), 38); } rstr = ""; sendform(); delay(1); // close the connection: CLIENT.stop(); } delay(20); } void sendform(){ // フォームを送る。 CLIENT.println("HTTP/1.1 200 OK"); CLIENT.println("Content-Type: text/html"); CLIENT.println("Connection: close"); CLIENT.println(); CLIENT.println("<!DOCTYPE HTML>"); File html = SD.open("irform.htm"); if (html) { while (html.available()) { CLIENT.write(html.read()); } html.close(); } }
勿論、ただのWebであるから、このようにスマホの他、タブレットなどからも操作可能である。また、ルータでポートフォワードすれば、外出先からの操作も可能である。
さて、前回のこのエントリでは、赤外線LEDでリモコンを作る基礎が整ったが、50mA定格のLEDに5mAしか流していないので、いまいち光り方に根性がなく、電気製品の受光部にかなり近づけないと厳しい。
そこで、どっぷり50mA近くまで電流を流し、赤外線LEDをして「ご主人様もうお腹いっぱいゲフォア」と言わしめたい。
運よく、以前にソレノイドを動かした時のFET「2SK2232」がある。
50mA近く流すには
……ということで、100Ωかましてやればいいが、ちょうど100Ωの抵抗は持っていない。で、330Ωがあるから、これを3本並列にして
110Ωに5V流せば
……というわけで、じゃぶじゃぶ流せる。2SK2232のドレイン電流は25Aなので、余裕でオッケーである。
スケッチは前回と同じでいい。
動かしてデジカメのモニタで観察すると、もう、ビカビカにLEDが光る光るッ!。そんなに受光部に近づけなくても、ばりばり扇風機のスイッチがオンオフできるようになった。
他の方のサイトでも、赤外線LEDを買ってきてArduinoで便利なリモコンを作る、ということをやっているので、私も真似したく、赤外線LEDを買ってきた。
10個も入っていて100円である。
受信機はこの前800円で買ったリモコンと受信モジュールのセットがあるから、これでよい。
この赤外線LEDは50mA流す規格なので、20mAしか流せないArduinoで光らせるには少し大き過ぎる。1kΩばかり抵抗をつけて5mAくらいにする。暗くてリモコンが反応しないのじゃないかな、とも思ったが、受光部に近づければ大丈夫だ。しかし、デジカメで観察するとかなり暗い。もし、ちゃんとしたものを組み立てるのなら、FETなどでリレーして、他の電源から50mA流したほうがいいだろう。
今回は遊びなので、5mAで光らせる。
先達のサイトでは、スクラッチビルドのコードで市販のリモコンの信号を受信し、ミリ秒単位でオンオフ時間を計測して記録し、その通りに赤外線LEDを光らせてうまく行っている方が多いようだ。
一方、Arduinoには、「IRremote」というライブラリをGitHubで公開しておられる方がいるので、これを使えば簡単である。
GitHubのページからzip玉をもらって解凍し、できたディレクトリ(IRremote)をArduinoのインストールディレクトリの「libraries」の下にコピーすればよい。
今日は私の家のリビングの扇風機のリモコンで試してみよう。
リビングの扇風機のリモコン信号をコピーしてテストするには、ライブラリをインストールするとできるサンプルスケッチのうち、「IRrecord」というのを使う。コマンドメニューは「ファイル→スケッチの例→IRremote→IRrecord」と辿る。
これは、「1回こっきりの学習リモコン」のスケッチだと思えばよい。市販のリモコンを受信して記憶し、次いでボタンを押すとそのままそっくり信号のコピーを赤外線LEDから送出するスケッチだ。
赤外線LEDは1kΩの抵抗と直列にしてデジタル3番ピンに、受信機は11番ピンに、タクトボタンを12番ピンにつなぐ。タクトボタンの入力側は10kΩの抵抗できちんとプルダウンしておくのが行儀がいい。そうしないと、手を触れたくらいのことでスイッチが入ってしまう。
ブレッドボードが出来たら、使いたいリモコンを受信機に近づけ、ボタンを押す。
それから今度は、ブレッドボード上の赤外線LEDを電気製品に近づけ、タクトボタンを押す。
そっくりコピーされた信号が送出され、扇風機が動く。
これで、ハードウェアは正しく動くことが分かったので、今度は単純にデータをハードコーディングで配列に書き出し、これを送出して扇風機のスイッチをオンオフさせる。
まず、データを記録する。それにはサンプルスケッチの「IRrecvDumpV2」を使う。今度は受信機を6番ピンにつなぎ換え、シリアルモニタを起動してデータを観測する。そうすると、そのまま使える形の配列の初期化定義の格好でダンプが出てくる。
これをクリップボードにコピーして、今度はサンプルスケッチの「IRSendDemo」にペーストし、送出してやればよい。
ところが、ここでハマッた、ハマッた。そのままではうまくいかないのである。
はじめ、私の扇風機では、「IRrecvDumpV2」のダンプはこうなった。
Encoding : UNKNOWN Code : 1FB3782D (32 bits) Timing[68]: -22598 +4500, -2150 + 650, - 500 + 600, - 500 + 500, - 600 + 550, - 550 + 650, -1600 + 600, -1600 + 550, -1700 + 600, - 500 + 550, -1700 + 600, -1600 + 600, -1650 + 550, - 550 + 600, -1600 + 600, - 500 + 650, - 500 + 600, - 500 + 600, -1600 + 650, - 500 + 600, - 500 + 600, - 500 + 600, - 500 + 600, - 500 + 650, -1600 + 600, - 500 + 600, - 500 + 550, - 600 + 600, -1600 + 600, -1600 + 550, -1700 + 600, -1600 + 650, - 500 + 500, - 600 + 550, unsigned int rawData[69] = {9627, 90,43, 13,10, 12,10, 10,12, 11,11, 13,32, 12,32, 11,34, 12,10, 11,34, 12,32, 12,33, 11,11, 12,32, 12,10, 13,10, 12,10, 12,32, 13,10, 12,10, 12,10, 12,10, 12,10, 13,32, 12,10, 12,10, 11,12, 12,32, 12,32, 11,34, 12,32, 13,10, 10,12, 11,0}; // UNKNOWN 1FB3782D
そこで私は、この「rawData[69]」をコピペし、次のようにした。
#include <IRremote.h> IRsend irsend; void setup() { Serial.begin(9600); } void loop() { static unsigned int rawData[] = {9627, 90,43, 13,10, 12,10, 10,12, 11,11, 13,32, 12,32, 11,34, 12,10, 11,34, 12,32, 12,33, 11,11, 12,32, 12,10, 13,10, 12,10, 12,32, 13,10, 12,10, 12,10, 12,10, 12,10, 13,32, 12,10, 12,10, 11,12, 12,32, 12,32, 11,34, 12,32, 13,10, 10,12, 11,0}; // UNKNOWN 1FB3782D if (Serial.read() != -1) { for (int i = 0; i < 3; i++) { irsend.sendRaw(rawData,sizeof(rawData) / sizeof(rawData[0]), 38); // Sony TV power code delay(40); } } }
ところが、これではまったくダメなのである。
最初は赤外線LEDの電流が足りないのかなあ、などと思い、半日ほどあっちこっちをいじくりまわして、ダメだった。
「IRrecord」での丸ごと信号コピーはうまく行っているのだから、電流が足りないわけではないらしい。
そこで、「IRrecord」のダンプ部分にコードを書き足し、「sendRaw()」関数にどんな値を渡しているかを見てみた。
else if (codeType == UNKNOWN /* i.e. raw */) { // Assume 38 KHz irsend.sendRaw(rawCodes, codeLen, 38); Serial.println("Sent raw"); Serial.println(codeLen); for(int cnt = 0; cnt <= codeLen; cnt++){ Serial.print(rawCodes[cnt]); Serial.print(","); } Serial.println("\n"); }
そうしたら、どうも、ダンプされた値の50倍が渡されているらしい。
4400,2250,550,600,500,600,500,600,500,600,550,1700,500,1700,500,1750,500,600,400,1800,550,1700,500,1650,550,600,550,1700,500,600,500,600,550,600,500,1700,550,550,450,650,550,600,500,600,500,600,450,1800,550,550,550,550,550,600,500,1700,550,1650,550,1700,500,1700,650,500,500,600,500,0,
そこで、「IRrecvDumpV2」のダンプ出力部分を次のようにカスタマイズした。
// Dump data for (int i = 0; i < results->rawlen; i++) { // Serial.print(results->rawbuf[i], DEC); Serial.print(results->rawbuf[i] * 50, DEC); Serial.print(","); if (!(i&1)) Serial.print(" "); }
そうすると、ダンプは……
ncoding : UNKNOWN Code : FBB100E8 (32 bits) Timing[68]: -24324 +4550, -2150 + 650, - 450 + 650, - 450 + 650, - 500 + 650, - 450 + 650, -1550 + 650, -1600 + 650, -1550 + 650, - 450 + 650, -1600 + 650, -1550 + 700, -1550 + 650, - 450 + 650, -1550 + 650, - 500 + 650, - 450 + 650, - 450 + 650, -1600 + 600, - 500 + 650, - 450 + 650, - 500 + 600, - 450 + 700, - 450 + 650, -1550 + 650, - 500 + 650, - 450 + 650, - 450 + 650, -1600 + 600, -1600 + 650, -1550 + 650, -1600 + 650, - 450 + 650, - 450 + 650, unsigned int rawData[69] = {24324, 4550,2150, 650,450, 650,450, 650,500, 650,450, 650,1550, 650,1600, 650,1550, 650,450, 650,1600, 650,1550, 700,1550, 650,450, 650,1550, 650,500, 650,450, 650,450, 650,1600, 600,500, 650,450, 650,500, 600,450, 700,450, 650,1550, 650,500, 650,450, 650,450, 650,1600, 600,1600, 650,1550, 650,1600, 650,450, 650,450, 650,0}; // UNKNOWN FBB100E8
……となる。
で、この先頭の「24324」は送信前の空き時間なので、捨ててよいようだ。「IRsendDemo」のほうにこれをコピペし、先頭の24324は消す。
#include <IRremote.h> IRsend irsend; void setup() { Serial.begin(9600); } void loop() { static unsigned int rawData[] = {4550,2150, 650,450, 650,450, 650,500, 650,450, 650,1550, 650,1600, 650,1550, 650,450, 650,1600, 650,1550, 700,1550, 650,450, 650,1550, 650,500, 650,450, 650,450, 650,1600, 600,500, 650,450, 650,500, 600,450, 700,450, 650,1550, 650,500, 650,450, 650,450, 650,1600, 600,1600, 650,1550, 650,1600, 650,450, 650,450, 650,0}; // UNKNOWN 1FB3782D if (Serial.read() != -1) { for (int i = 0; i < 3; i++) { irsend.sendRaw(rawData,sizeof(rawData) / sizeof(rawData[0]), 38); // Sony TV power code delay(40); } } }
これで、赤外線LEDを扇風機に近づけ、シリアルモニタの「送信」ボタンをクリックすると、扇風機がオン・オフされる。
暑くなると幽霊譚、怪談、オカルトの話に傾くのは、涼味をなんとか得ようとする昔の人の知恵ででもあったろうか。
この季節、雑談などするとつい話もソッチへ傾きがちで、馬鹿げているがこれはこれで夏らしくてよい。
幽霊談、心霊現象自慢、怪奇現象自慢などをしていると思い出すことがある。
小学校の2、3年、あるいは4年くらいの頃だったか。子供の頃の私は、女の子に笑われてしまうほどの「怖がり」であった。なにしろ、なによりも幽霊が怖い。長じてみると幽霊なんぞより生身の人間のほうがよっぽど怖いということが身にしみてわかるようになるが、子供は生身の人間の怖さなど知らないから、幽霊なんぞを怖がるのである。
子供の頃の私が住んでいた家は大正時代に建てられた古い長屋官舎で、戦災の焼け残りだった。古びた土壁、暗い田の字型の室内、北向きの縁側、間取りは4畳と6畳のふた間。そこに5人家族がひしめき合って暮らしていた。まあ、裕福ではなかった。
古い官舎の天井には、なにか人の手形のようなものが付着していて、それがなにやら茶色く変色しており、「血の跡なのでは……!」と想像しだすとキリがなく怖い。裏の物置から幽霊が出てきそうで、本当に怖かった。
私は末っ子だったので、学校から帰ってくるのは一番早い。母が働きに行っていたから、当然、誰もいない家に一人で鍵をあけて入る。暗い官舎にじっとしていると、幽霊があっちからこっちから出てくるような気がして、もう、逃げ出したくなってくる。
それで、恐ろしくてたまらず、ともかく家から走り出す。走り出したからといって特段することなどなく、当時多くの人がやっていた暇つぶし、商店街の本屋へ立ち読みに行く。
怖くて家を出てきたのだから、漫画の立ち読みでもして気分を明るくすればいいようなものなのに、そんな時に限って、ふと立ち止まって見上げた本棚には、「恐怖の心霊写真集・パート1」などという、当時はやった本が並んでいるのである。それをまた、よせばいいのに、つい、ついつい、おずおずと手にとってしまう私なのであった。
ページをめくる怖さ恐ろしさ。やめときゃいいのに、ずんずんページをめくってしまい、背に冷や汗が流れ、本屋のレジのウラの暗がりから今にも霊がまろび出て来そうな気がして、悲鳴を上げそうになるくらい怖い。ああ、こんな怖いものを見ているくらいなら、とっとと家へ帰ろう!
そうして本屋を逃げ出して、小走りに家に帰るのだが、一人っきりの家がまたしても怖い。さっきまで見ていた「心霊写真集」の霊が台所の障子から浮かび上がってきそうな気がする。うひゃあ!怖い!
それでまた、家を飛び出し、これがどういう脳みその構造だか、また本屋に行って、また「心霊写真集」を手にとってしまうという、ワケの分からない私なのであった。ひい、怖い、……と、また家に走って帰り、また怖くて、友達の家にでも行けばいいのに本屋に行ってしまい、また心霊写真集を見、また家に駆け戻り、……という、「猿のなんちゃら……」のような、アホなことを5、6回も繰り返すのである。
我ながら、本当に変な子だったなあ。
そういう、怖がりにバカの混じった私であったが、長じてから職業上、人が首を吊って死んだ跡地で闇夜に一晩見張りをしたり、災害地で沢山のご遺体を収容したりするうち、幽霊なんかまったく怖くなくなってしまった。
また、テレビでアナウンサーが「台風が上陸する可能性があります」と言っていた。
どうも、いただけない。災害などは「上陸するおそれがあります」「土砂災害が起こるおそれがあります」と言ってほしい。
忌むべきものに
「あとうべく……」「あとうべかる……」
……は、やっぱり違うと思う私である。ここはやはり「おそれ」だろう。
「可能性」という言葉は、
「彼は合格する可能性が高い」
というふうに使うのがよい。
庭の草むしりをして、たくさん蚊に刺された。痒い。
蚊に刺されたくらいのこと、以前であれば、刺されたのがたとえ顔であろうと、女ではないのだから、血が出るくらいボリボリ掻きむしっておけば、特段のことはせずとも治ったのであって、何も気にすることはなかった。
だが、最近は気温の変化などで熱帯性の病原をもった蚊がいるから油断できない。
知人の息子さんも、たくさん蚊に刺されたあと、高熱を発して救急車を呼ぶ騒ぎになったそうで、デング熱が問題になる少し前のことだったから「原因不明のウィルス感染症」というようなことで、安静により治癒したそうだが、こうなってくると気温の変化も捨ててはおけぬ気がする。
私も後々「デング熱」など出なければよいが。
さえずり季題【241】は「グラジオラス」と致しました。傍題に唐菖蒲、和蘭菖蒲、和蘭あやめ。小学生も知っている花、という印象ですね。オモチャのような名が、いっそう暑さを増しそうですが。「グラヂオラス妻は愛憎鮮烈に」 (日野草城) #saezuriha #saezuriha_odai
— boubun (@boubun) July 17, 2015
秋月電子のサイトを見ていると、小さなカメラがあり、Arduinoに付きそうな感じだ。
3850円。Arduino自体が2800円かそこらなので、それに比べるとちょっと高いが、早速行って購入。
だが、あまり情報は多くない。まず、メーカーのサイトを見ていくと、チュートリアルがあり、「とりあえずテストするには、電源をくれてやって、一番端のピンをテレビにつなぎゃあ絵が出る」みたいなザックリ感満載の解説が。それで、テレビにつなぐためのRCAジャックなども買う。
チュートリアルはこれを読んでおけばだいたいいいようだ。
ほどいてみるとこんな感じで、かなり小さい。
ピンのピッチが2mmで、ブレッドボードで扱いにくい。それで、普通の2.54mmのピンヘッダを出して、その根元をこんなふうにムリヤリ(笑)2mmピッチにせばめる。
こいつをカメラの基盤にえいやっ、とねじ込み、半田付けする。
なかなか小さいので、ルーペと老眼鏡を併用しつつ、ICなんか壊しちゃってもナンだから、20Wのぬるくて細い半田鏝でさっさとつける。
我ながらなかなかスピーディな仕事だなあ(笑)。
で、メーカーのサイトには「5V」と書いてあるが、これは互換品の別の製品のためのチュートリアルのようで、買ってきたものの基盤をよく見ると「3.3V」と印刷されている。壊してはもったいないから、3.3Vで試す。3.3Vの電源代わりにArduinoの3.3Vピンを使う。
基盤の印刷通り、3.3V、GND、それから右端のピンをRCAジャックのセンターに、RCAジャックのアースを同じくGNDに入れて、テレビの前に持っていく。
テレビにつなぐと、おお、確かに、値段なりのフザけた画質(笑)で、自分の顔が映る。
上下が逆だが、まあ、いいや。
で、今度はArduinoで画像を撮影してみよう。
チュートリアルにしたがってArduino用のライブラリをダウンロードし、これをArduinoのインストールフォルダの「libraries」に配置する。
そうしておいてArduinoのIDEを起動すると、「ファイル」→「スケッチの例」の中に「Adafruit VC0706 Serial Camera Library」が現れるから、この中から「Snapshot」を選ぶ。これは静止画をjpegで撮影するスケッチのサンプルだ。
// This is a basic snapshot sketch using the VC0706 library. // On start, the Arduino will find the camera and SD card and // then snap a photo, saving it to the SD card. // Public domain. // If using an Arduino Mega (1280, 2560 or ADK) in conjunction // with an SD card shield designed for conventional Arduinos // (Uno, etc.), it's necessary to edit the library file: // libraries/SD/utility/Sd2Card.h // Look for this line: // #define MEGA_SOFT_SPI 0 // change to: // #define MEGA_SOFT_SPI 1 // This is NOT required if using an SD card breakout interfaced // directly to the SPI bus of the Mega (pins 50-53), or if using // a non-Mega, Uno-style board. #include <Adafruit_VC0706.h> #include <SPI.h> #include <SD.h> // comment out this line if using Arduino V23 or earlier #include <SoftwareSerial.h> // uncomment this line if using Arduino V23 or earlier // #include <NewSoftSerial.h> // SD card chip select line varies among boards/shields: // Adafruit SD shields and modules: pin 10 // Arduino Ethernet shield: pin 4 // Sparkfun SD shield: pin 8 // Arduino Mega w/hardware SPI: pin 53 // Teensy 2.0: pin 0 // Teensy++ 2.0: pin 20 #define chipSelect 10 // Pins for camera connection are configurable. // With the Arduino Uno, etc., most pins can be used, except for // those already in use for the SD card (10 through 13 plus // chipSelect, if other than pin 10). // With the Arduino Mega, the choices are a bit more involved: // 1) You can still use SoftwareSerial and connect the camera to // a variety of pins...BUT the selection is limited. The TX // pin from the camera (RX on the Arduino, and the first // argument to SoftwareSerial()) MUST be one of: 62, 63, 64, // 65, 66, 67, 68, or 69. If MEGA_SOFT_SPI is set (and using // a conventional Arduino SD shield), pins 50, 51, 52 and 53 // are also available. The RX pin from the camera (TX on // Arduino, second argument to SoftwareSerial()) can be any // pin, again excepting those used by the SD card. // 2) You can use any of the additional three hardware UARTs on // the Mega board (labeled as RX1/TX1, RX2/TX2, RX3,TX3), // but must specifically use the two pins defined by that // UART; they are not configurable. In this case, pass the // desired Serial object (rather than a SoftwareSerial // object) to the VC0706 constructor. // Using SoftwareSerial (Arduino 1.0+) or NewSoftSerial (Arduino 0023 & prior): #if ARDUINO >= 100 // On Uno: camera TX connected to pin 2, camera RX to pin 3: SoftwareSerial cameraconnection = SoftwareSerial(2, 3); // On Mega: camera TX connected to pin 69 (A15), camera RX to pin 3: //SoftwareSerial cameraconnection = SoftwareSerial(69, 3); #else NewSoftSerial cameraconnection = NewSoftSerial(2, 3); #endif Adafruit_VC0706 cam = Adafruit_VC0706(&cameraconnection); // Using hardware serial on Mega: camera TX conn. to RX1, // camera RX to TX1, no SoftwareSerial object is required: //Adafruit_VC0706 cam = Adafruit_VC0706(&Serial1); void setup() { // When using hardware SPI, the SS pin MUST be set to an // output (even if not connected or used). If left as a // floating input w/SPI on, this can cause lockuppage. #if !defined(SOFTWARE_SPI) #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) if(chipSelect != 53) pinMode(53, OUTPUT); // SS on Mega #else if(chipSelect != 10) pinMode(10, OUTPUT); // SS on Uno, etc. #endif #endif Serial.begin(9600); Serial.println("VC0706 Camera snapshot test"); // see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); // don't do anything more: return; } // Try to locate the camera if (cam.begin()) { Serial.println("Camera Found:"); } else { Serial.println("No camera found?"); return; } // Print out the camera version information (optional) char *reply = cam.getVersion(); if (reply == 0) { Serial.print("Failed to get version"); } else { Serial.println("-----------------"); Serial.print(reply); Serial.println("-----------------"); } // Set the picture size - you can choose one of 640x480, 320x240 or 160x120 // Remember that bigger pictures take longer to transmit! cam.setImageSize(VC0706_640x480); // biggest //cam.setImageSize(VC0706_320x240); // medium //cam.setImageSize(VC0706_160x120); // small // You can read the size back from the camera (optional, but maybe useful?) uint8_t imgsize = cam.getImageSize(); Serial.print("Image size: "); if (imgsize == VC0706_640x480) Serial.println("640x480"); if (imgsize == VC0706_320x240) Serial.println("320x240"); if (imgsize == VC0706_160x120) Serial.println("160x120"); Serial.println("Snap in 3 secs..."); delay(3000); if (! cam.takePicture()) Serial.println("Failed to snap!"); else Serial.println("Picture taken!"); // Create an image with the name IMAGExx.JPG char filename[13]; strcpy(filename, "IMAGE00.JPG"); for (int i = 0; i < 100; i++) { filename[5] = '0' + i/10; filename[6] = '0' + i%10; // create if does not exist, do not open existing, write, sync after write if (! SD.exists(filename)) { break; } } // Open the file for writing File imgFile = SD.open(filename, FILE_WRITE); // Get the size of the image (frame) taken uint16_t jpglen = cam.frameLength(); Serial.print("Storing "); Serial.print(jpglen, DEC); Serial.print(" byte image."); int32_t time = millis(); pinMode(8, OUTPUT); // Read all the data up to # bytes! byte wCount = 0; // For counting # of writes while (jpglen > 0) { // read 32 bytes at a time; uint8_t *buffer; uint8_t bytesToRead = min(32, jpglen); // change 32 to 64 for a speedup but may not work with all setups! buffer = cam.readPicture(bytesToRead); imgFile.write(buffer, bytesToRead); if(++wCount >= 64) { // Every 2K, give a little feedback so it doesn't appear locked up Serial.print('.'); wCount = 0; } //Serial.print("Read "); Serial.print(bytesToRead, DEC); Serial.println(" bytes"); jpglen -= bytesToRead; } imgFile.close(); time = millis() - time; Serial.println("done!"); Serial.print(time); Serial.println(" ms elapsed"); } void loop() { }
で、これはSDカードに書き込むようになっている。
私の手持ちの、ArduinoにSDカードをつなぐ手段は、先日から愛用中の「ETHERNET SHIELD 2」に搭載されているSDカードスロットだけだから、とりあえずこれを使う。
手持ちのSDカードをETHERNET SHIELD 2に挿し、Adafruitのサイトのチュートリアルを参考に回路をブレッドボードに組む。
注意する点は2つだ。
そうやってArduinoをスタートさせると、写真が1枚だけ撮れる。
下は、そうやって撮った私の顔である。
……むっちゃむさくるしいなあw。