Webでサービスできるカメラ

投稿日:

 一昨日遊んだカメラ。今日はこれにETHERNET SHIELD 2を取り付けて遊んでみる。

  •  間欠的に写真を撮り続ける。
  •  SDに保存する。
  •  ウェブアクセスがあると、一覧でサービスする。

 まあ、これくらいで。サムネイル画像などハデに出したい気もするが、Arduinoはメモリも小さく、そんなに気の利いたウェブサービスはできない。

 プロトタイピングシールドのブレッドボードに組み付ける。

IMG_3136

 まず、スケッチはこんなところだろう。

//
//  camera2Web.ino
//    27.07.20(月) 0850~
//    佐藤俊夫
//    Adafruit製小型TTLシリアルJPEGカメラ+ETHERNET SHIELD 2で
//    間欠撮影をし、Webでダウンロードできるようにする。
//
#include <Adafruit_VC0706.h>
#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>
#include <Ethernet2.h>

#define CHIPSELECT 4

SoftwareSerial CAMCONNECTION(2, 3);
Adafruit_VC0706 CAM = Adafruit_VC0706(&CAMCONNECTION);
const unsigned long int INTERVAL = 30L * 1000L;
byte MAC[] = {  0x90, 0xA2, 0xDA, 0x0F, 0xF6, 0x74 };
IPAddress IP(192, 168, 1, 129);
EthernetServer SERVER(80);
EthernetClient CLIENT;

void setup() {
  pinMode(10, OUTPUT);
  if(!SD.begin(CHIPSELECT)) return;
  if(!CAM.begin()) return;
  CAM.setImageSize(VC0706_320x240);
  Ethernet.begin(MAC, IP);
  SERVER.begin();
  delay(1000);
}

void loop() {
  static unsigned long int prevtime = 0;
  char c;
  String rstr = "";
  //  INTERVALおきに写真を撮る
  if(millis() >= prevtime + INTERVAL){
    prevtime = millis();
    takePicture();
  }
  //  Webサーバ
  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("IMG") >= 0){
      String filename = "DCIM/";
      char cfilename[17];
      filename.concat(rstr.substring(rstr.indexOf("IMG"), rstr.indexOf("JPG") + 3));
      filename.toCharArray(cfilename, 17);
      CLIENT.println("HTTP/1.1 200 OK");
      CLIENT.println("Content-Type: image/jpg");
      CLIENT.println("Connection: close");
      CLIENT.println();
      File img = SD.open(cfilename);
      while(img.available()){
        CLIENT.write(img.read());
      }
      img.close();
    }else{
      sendform();
    }
    rstr = "";
    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>");
  CLIENT.println("<html><head></head><body><center>");
  File dcim = SD.open("/DCIM");
  while(true) {
    File imgfile =  dcim.openNextFile();
    if(!imgfile){
      dcim.rewindDirectory();
      break;
    }
    CLIENT.write("<a href=\"");
    CLIENT.write(imgfile.name());
    CLIENT.write("\">");    
    CLIENT.write(imgfile.name());
    CLIENT.println("</a><br>");
    imgfile.close();    
  }
  dcim.close();
  CLIENT.println("</center></body></html>");
}

void takePicture(){
  static unsigned int pnum = 0;
  char filename[] = "DCIM/img0000.jpg";
  CAM.takePicture();
  filename[8]  = '0' + pnum / 1000;
  filename[9]  = '0' + (pnum / 100) % 10;
  filename[10] = '0' + (pnum /  10) % 10;
  filename[11] = '0' + pnum % 10;
  pnum ++;
  if(pnum > 9999) pnum = 0;
  if(SD.exists(filename)) return;
  File imgFile = SD.open(filename, FILE_WRITE);
  uint16_t jpglen = CAM.frameLength();
  pinMode(8, OUTPUT);
  while (jpglen > 0) {
    uint8_t *buffer;
    uint8_t bytesToRead = min(32, jpglen);
    buffer = CAM.readPicture(bytesToRead);
    imgFile.write(buffer, bytesToRead);
    jpglen -= bytesToRead;
  }
  imgFile.close();
}

  で、電源をつないで放置しておくと、写真が撮れるわけだ。既存のファイル名を避けるような芸コマなことはしてないけど、これはこれでけっこう面白い。

 いつもの100円ショップのアクリル枠にねじ止めする。このカメラは上下が逆に映るので、こうすると丁度カメラが逆さまになって具合が良い。

IMG_3138

 それで、以前にWIZnetで紹介されて愉快だったアレのリベンジをやってみよう。携帯用のカメラホルダーに取り付け、ベランダに出して放置。

IMG_3139

統一が正しいとは限らない

投稿日:

 免許証とSuicaと住基カードとTポイントカードとVISAと銀行キャッシュカードを全部1枚におさめることは、技術上は可能なのだが、「絶対ダメーーーーーっ!」と絶叫するものすごい人々がいるので、ダメなのだろうなあ。

 全部1枚になったほうがよっぽど便利だと思うが、便利と言うものはだいたいは人生の敵だから、まあ、カードがバラバラにあるのは、それはそれでよい。

いよいよ多機能リモコン

投稿日:

 LEDを強力に光らせることが出来たので、いよいよWebサーバつき多機能リモコンを作成する。卓上などに置いておき、ネットワークにつないで、スマホなどから複数の電化製品を操作できるというものだ。

 ここでは、いくつかのテクニックを使った。

 一つは、フォームが大きくなってしまい、ハードコーディングするとメモリが足りなくなる。そこで、SDカード内にHTMLを置き、これを読み出すようにした。

 同様に、リモコンから読み取った数値データが大きくなって、普通にハードコーディングしたのではメモリが不足する。そこで、「PROGMEM」というキーワードを使って、フラッシュメモリ内にデータを置き、これを読み出すようにした。

IMG_3134 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>

irform 上の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();
  } 
}

IMG_3135 勿論、ただのWebであるから、このようにスマホの他、タブレットなどからも操作可能である。また、ルータでポートフォワードすれば、外出先からの操作も可能である。

赤外線LEDをもっとビカビカ光らせる

投稿日:

 さて、前回のこのエントリでは、赤外線LEDでリモコンを作る基礎が整ったが、50mA定格のLEDに5mAしか流していないので、いまいち光り方に根性がなく、電気製品の受光部にかなり近づけないと厳しい。

 そこで、どっぷり50mA近くまで電流を流し、赤外線LEDをして「ご主人様もうお腹いっぱいゲフォア」と言わしめたい。

 運よく、以前にソレノイドを動かした時のFET「2SK2232」がある。

 50mA近く流すには

R=\cfrac{5V}{50mA}=100\Omega

……ということで、100Ωかましてやればいいが、ちょうど100Ωの抵抗は持っていない。で、330Ωがあるから、これを3本並列にして

\cfrac{1}{\cfrac{1}{330\Omega}\times 3}=110\Omega

 110Ωに5V流せば

\cfrac{5V}{110\Omega}\fallingdotseq 45mA

……というわけで、じゃぶじゃぶ流せる。2SK2232のドレイン電流は25Aなので、余裕でオッケーである。

IMG_3133 と、いうわけで、回路図とブレッドボードはこうなる。

 スケッチは前回と同じでいい。

 動かしてデジカメのモニタで観察すると、もう、ビカビカにLEDが光る光るッ!。そんなに受光部に近づけなくても、ばりばり扇風機のスイッチがオンオフできるようになった。

赤外線LEDでリモコンごっこ

投稿日:

IMG_3128 他の方のサイトでも、赤外線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を扇風機に近づけ、シリアルモニタの「送信」ボタンをクリックすると、扇風機がオン・オフされる。

IMG_3129 ブレッドボードは結局、最後はこんな感じ。

幽霊怖いよ

投稿日:

 暑くなると幽霊譚、怪談、オカルトの話に傾くのは、涼味をなんとか得ようとする昔の人の知恵ででもあったろうか。

 この季節、雑談などするとつい話もソッチへ傾きがちで、馬鹿げているがこれはこれで夏らしくてよい。

 幽霊談、心霊現象自慢、怪奇現象自慢などをしていると思い出すことがある。

 小学校の2、3年、あるいは4年くらいの頃だったか。子供の頃の私は、女の子に笑われてしまうほどの「怖がり」であった。なにしろ、なによりも幽霊が怖い。長じてみると幽霊なんぞより生身の人間のほうがよっぽど怖いということが身にしみてわかるようになるが、子供は生身の人間の怖さなど知らないから、幽霊なんぞを怖がるのである。

 子供の頃の私が住んでいた家は大正時代に建てられた古い長屋官舎で、戦災の焼け残りだった。古びた土壁、暗い田の字型の室内、北向きの縁側、間取りは4畳と6畳のふた間。そこに5人家族がひしめき合って暮らしていた。まあ、裕福ではなかった。

 古い官舎の天井には、なにか人の手形のようなものが付着していて、それがなにやら茶色く変色しており、「血の跡なのでは……!」と想像しだすとキリがなく怖い。裏の物置から幽霊が出てきそうで、本当に怖かった。

 私は末っ子だったので、学校から帰ってくるのは一番早い。母が働きに行っていたから、当然、誰もいない家に一人で鍵をあけて入る。暗い官舎にじっとしていると、幽霊があっちからこっちから出てくるような気がして、もう、逃げ出したくなってくる。

 それで、恐ろしくてたまらず、ともかく家から走り出す。走り出したからといって特段することなどなく、当時多くの人がやっていた暇つぶし、商店街の本屋へ立ち読みに行く。

 怖くて家を出てきたのだから、漫画の立ち読みでもして気分を明るくすればいいようなものなのに、そんな時に限って、ふと立ち止まって見上げた本棚には、「恐怖の心霊写真集・パート1」などという、当時はやった本が並んでいるのである。それをまた、よせばいいのに、つい、ついつい、おずおずと手にとってしまう私なのであった。

 ページをめくる怖さ恐ろしさ。やめときゃいいのに、ずんずんページをめくってしまい、背に冷や汗が流れ、本屋のレジのウラの暗がりから今にも霊がまろび出て来そうな気がして、悲鳴を上げそうになるくらい怖い。ああ、こんな怖いものを見ているくらいなら、とっとと家へ帰ろう!

 そうして本屋を逃げ出して、小走りに家に帰るのだが、一人っきりの家がまたしても怖い。さっきまで見ていた「心霊写真集」の霊が台所の障子から浮かび上がってきそうな気がする。うひゃあ!怖い!

 それでまた、家を飛び出し、これがどういう脳みその構造だか、また本屋に行って、また「心霊写真集」を手にとってしまうという、ワケの分からない私なのであった。ひい、怖い、……と、また家に走って帰り、また怖くて、友達の家にでも行けばいいのに本屋に行ってしまい、また心霊写真集を見、また家に駆け戻り、……という、「猿のなんちゃら……」のような、アホなことを5、6回も繰り返すのである。

 我ながら、本当に変な子だったなあ。

 そういう、怖がりにバカの混じった私であったが、長じてから職業上、人が首を吊って死んだ跡地で闇夜に一晩見張りをしたり、災害地で沢山のご遺体を収容したりするうち、幽霊なんかまったく怖くなくなってしまった。

可能性

投稿日:

 また、テレビでアナウンサーが「台風が上陸する可能性があります」と言っていた。

 どうも、いただけない。災害などは「上陸するおそれがあります」「土砂災害が起こるおそれがあります」と言ってほしい。

 忌むべきものに

「あとうべく……」「あとうべかる……」

……は、やっぱり違うと思う私である。ここはやはり「おそれ」だろう。

 「可能性」という言葉は、

「彼は合格する可能性が高い」

というふうに使うのがよい。

投稿日:

 庭の草むしりをして、たくさん蚊に刺された。痒い。

 蚊に刺されたくらいのこと、以前であれば、刺されたのがたとえ顔であろうと、女ではないのだから、血が出るくらいボリボリ掻きむしっておけば、特段のことはせずとも治ったのであって、何も気にすることはなかった。

 だが、最近は気温の変化などで熱帯性の病原をもった蚊がいるから油断できない。

 知人の息子さんも、たくさん蚊に刺されたあと、高熱を発して救急車を呼ぶ騒ぎになったそうで、デング熱が問題になる少し前のことだったから「原因不明のウィルス感染症」というようなことで、安静により治癒したそうだが、こうなってくると気温の変化も捨ててはおけぬ気がする。

 私も後々「デング熱」など出なければよいが。

グラジオラス

投稿日: