青空文庫一括ダウンロード

投稿日:

 驚いた。それで、GitHubに行ったらほんとうにそうだ。

最近の一般向けワンボード・コンピュータ、なかんづく「Arduino」「Raspberry Pi」の状況

投稿日:

平成28年2月
JISTA ML リレーコラム 第1回
会員 関東支部 jista1197 佐藤俊夫

1 はじめに

 このところ、若い方やDIYなどを楽しむ一般の方向けに、ワンボード・コンピュータが売られています。しかも、相当な売れ行きであると仄聞(そくぶん)します。

 「Arduino」や「Raspberry Pi」、「IchigoJam」「Intel Edison」「mbed」と言った製品名を聞いたことがある方も多いでしょう。秋葉原を歩くと、店の表にこれらのカラフルな箱が山積みになっています。また、イタリア人マッシモ・バンジ氏の「TED」スピーチを見たことがある方もたくさんおられると思います。

 数年前から、普及価格帯の3Dプリンタが出現してきたことにより、一般人でも小規模の製品を作り、それを世に問うようなことが可能になってきました。そこから、「Makers Movement」と言われる流行が盛り上がりはじめました。これはオープンソース・イノベーションの自然な興隆ということに加えて、出版と連携した一種のマーケティングの成功であるとも言われているようです。ちなみに、この「Makers Movement」について著書を出し、流行に火をつけたのはクリス・アンダーソンと言う人ですが、この人の名を覚えている方もJISTA会員の皆さんには多いことでしょう。そう、かの「ロング・テール」の著者その人です。

 古くから言われる「DIY」と、最新流行の「Makers Movement」との違いは、そこに「ネット」が介在するか否かだ、と言ってもよいと思います。物を作る上での発想やハウ・トゥ、設計図、使用感などは、インターネットを通じて迅速に共有され、改良が加えられ、更に共有されます。

 こうしたことを背景に、3Dプリンタを使用してものを作り、作ったものへデジタルやITのパワーを盛り込むことが広く行われるようになりました。ものへデジタルやITのパワーを盛り込むのには、廉価なワンボード・コンピュータがうってつけだというわけです。

 実際、巷間ではArduinoがこれまでに100万台、Raspberry Piが700万台売れたと言われていますから、これらのワンボード・コンピュータは、ITのプラットフォームとして無視できない勢力になっていると言えます。また、これらは安価に数を得られることから、「IoT」流行の趨勢とも関連する雰囲気が濃厚に感じられます。

 私は仕事の上ではこれらと関わっておりません。しかし、その一方で、ITストラテジの道というものは、ITワールドを形作るもののうちの何が将来の影響要素としてつながってくるかわかりません。そうしたことから、これらワンボード・コンピュータを体験しておくことが何かの足しになることもあろうと思い、ArduinoとRaspberry Piを少しばかりいじってみておりましたところ、ちょうど今般リレーコラム執筆の機会を頂きました。

 そこで、この機会をお借りして、ArduinoとRaspberry Piについて、その状況などを簡単に紹介させていただきたいと思います。

2 Arduinoについて

 「Arduino」はイタリア人のマッシモ・バンジ氏を中心に、平成17年(2005)から開発が始められたワンボード・コンピュータです。バージョンアップや派生を含め、既に数十種類の製品があります。現在店頭で入手可能な製品には、代表的な「Arduino UNO」があり、これは本日(平成28年2月7日)現在、税込み2,940円で手に入ります。

 その特徴に、次のようなことを挙げることができます。

○ ハードウェアが「オープンソース」であること。

 仕様も設計もオープンで、一般人がネットから基盤のパターンなどをダウンロードしてそっくり同じものを製作することも可能です。また、そのため、世界中にArduinoのクローンを作っている企業やグループがあり、それら自由なクローンの種類を数え上げることはもはや不可能と言う状況にあります。

○ 開発環境が無料で配布されていること。

 開発環境は統合環境(IDE)になっており、これも無料です。様々なライブラリが日々提供され続けており、一般に入手可能なほとんどのハードウェアやIC、電子素子などを扱うためのライブラリがGitHubなどですぐに手に入ります。

○ アナログ入出力、デジタル入出力が簡単に可能であること。

 「Arduino UNO」の場合、基板上にはデジタルI/Oが14本、アナログINが6本、アナログOUTが6本あります。これにさまざまな電子部品をつないで制御することができます。

○ C++で簡単にプログラミングできること。

 従来の「マイコン」はアセンブラ一辺倒でしたが、ArduinoはC++でプログラミングでき、しかも、ややこしいハードウェア・アクセスはC++のクラスの中にすべてカプセル化され、手軽に扱うことが可能になっています。ユーザはオブジェクトを生成し、入出力ピンを開いて読み、書くだけでさまざまなことができます。

 こうした特徴があるため、Arduinoを使用すると、一般人がデジタル・プロダクトを簡単に製作することができます。

 かく言う私も次のようなことを試しました。

「スマート・ファン」

 安物の扇風機にArduinoとEtehrnetインターフェース、温度センサをとりつけ、Webサーバをこの扇風機に実装してブラウザから制御できるようにし、温度によって風量を変え、1/fゆらぎ送風ができるなど、ものすごく無駄にリッチ化した扇風機です。

 1980円の安物扇風機をめっちゃ高機能化(笑)して、結構遊べました。

ウェブ扇風機

「メールサーバ監視ランプ」

 メール来着状況を監視し、ランプの色で教えてくれるもの。昔はISDN用のルータにこうした機能があり、パソコンのスイッチを入れていなくてもメールチェックのタイミングを教えてくれて便利だったのですが、今はこういうものがなくなってしまったので、意を決して自作したわけです。実は、今はスマホでこれは出来てしまうのですが、LEDの輝度のパルス幅変調などを試して遊ぶのに格好の題材でしたので、あえて試してみました。

メールサーバお知らせランプ

「多機能リモコン」

 赤外線リモコンのある電化製品を、Webで制御するようにしたものです。パソコンからでも、スマホからでも操作できます。赤外線LEDは秋葉原などで100円ほどで手に入りますので、これを試す人は非常に多いようです。

いよいよ多機能リモコン

「モーターで動くおもちゃの類をデジタル・パワードにする」

 おもちゃにArduinoを積み込むと、男の子などは大変喜びます。ここでは、モーターで動く自動車に超音波センサをとりつけ、自律制御させるとともに、その自動車にデジカメを乗せて動画を撮影しています。

虫瞰(ちゅうかん)カメラ

 私には男の子供はありませんが、私自身が男の子じみた中年(笑)ですので、みずからこういうことを試して遊びました。

 このように、Arduinoを使うと、手軽にさまざまな機器を制御したり、デジタルパワーを盛り込んだものを作ることが可能です。

 伝統的にArduinoにはAtmel社のマイコン「AVR」シリーズが使われていましたが、最新の製品「Arduino 101」にはIntelのCurieが採用され、Bluetoothや6軸の加速度ジャイロセンサーが搭載されるなど、大変高機能化しています。

 さて、目が離せないArduinoですが、昨年ごろから、長年Arduinoを牽引してきた5人の人たちが仲違いし、分裂騒動を起こしてまだ決着がつかず、もめているようです。Arduinoコミュニティは既に大きなものになっているので、この騒動は残念なことであり、かつ、目が離せないところです。

3 Raspberry Piについて

 「Raspberry Pi」はイギリス人のイブン・アプトン氏を中心に、Raspberry Pi財団というところが開発しているワンボード・コンピュータです。SONYなども深くかかわっていると聞き及びます。

 平成24年(2012)に最初のモデルが発売され、バージョンアップ等で数種類の製品があります。現在店頭で入手可能な製品の代表的なものに「Raspberry Pi 2 Model B+」があり、これは本日(平成28年2月7日)現在で税込み5,000円です。

 次のような特徴があります。

○ れっきとした「PC」であること。

 前述のArduinoは「マイコンボード」であり、OS等は載っておりません。しかし、Raspberry PiはSoCを利用するれっきとした「パソコン」で、OSを載せて作動します。主としてRaspberry Pi用に最適化されたLinux(「Raspbian」というディストリビューション)が作動するほか、マイクロソフトからは「Windows 10 IoT core」というWindows製品が、なんと無料でコントリビュートされています。余談、最近のマイクロソフト社の変革ぶり、オープンソースやフリーへのコミットぶりには驚くばかりです。それだけ、IoTにからむ製品に注目しているということでしょう。

 また、HDMI端子やUSB、Ethernetの端子を基板上に標準で備えているので、キーボードやマウス、ディスプレイをつないでPCとして利用することができます。

○ あらゆる開発環境が利用可能なこと。

 「Linuxマシン」なので、Linuxで利用できる開発ツール類は全て利用できると言って過言ではありません。Raspberry Piになじみの良いのはPythonで、入門本などにはPythonの作例が多く載っております。他にRaspberry Pi向けに最適化されたビジュアル言語の「Scratch」などもあります。しかし、別にこれらにこだわる必要はなく、GCCが走りますから、C/C++も使えますし、他にシェル、PerlやPHP、Ruby、Javaも扱えます。極端な話、g77をインストールして「FORTRAN」でハードウェア制御を行うことも可能でしょう(聞いたことはありませんが……)。私などは、RubyやPythonに暗いので、CやPHPでRaspberry Piのプログラムを書きました。

 エディタやIDEも、自分が使い慣れた好きなものが使えます。私はviが好きなので、Raspberry Pi上でももっぱらviを使っています。

○ 簡単にデジタル入出力が可能なこと。

 「GPIO」と呼ばれるデジタル入出力端子を豊富に備えていますが、これらは、ユーザからはUNIXで言う所の「ファイルシステム上にあるスペシャル・ファイル」に見えます。ですので、このファイルをオープンし、読み、あるいは書くだけで外部に入出力ができ、ハードウェアの制御が可能です。

○ あらゆるミドルウェア等が利用可能なこと。

 Linuxであるがゆえ、Linuxで使えるミドルウェアなどはほとんどのものが利用可能です。例えば、MySQLやPostgreSQLなどのRDBも扱えます。また、ApacheなどのWebサーバ、sendmail、DovecotなどのPOP/IMAPサーバなども走ります。

 こうした特徴があるため、既存のオープンソース・ソフトウェアを用いて、相当複雑なことも可能です。

 Arduinoと違って、Raspberry Piは単体ではアナログ入力ができません。そこで、私はRaspberry PiにADC(アナログ・デジタル変換)のICを接続し、これにサーミスタを取り付け、Apache+PHP+C言語を使用して「ウェブ温度計の製作」などを試してみました。

Raspberry Piで温度を測ろう

 また、PHPでハードウェア制御もできます。私はこんなふうに、PHPを使用して、Webインターフェイスにより家電製品のスイッチをオン・オフすることなどを試しました。

そうか、PHPでもモノにつながるな

 Arduinoに比べてRaspberry Piの歴史は浅いのですが、その出荷台数などから見ても新進気鋭の勢いを持っており、躍進中であると言えます。

 もともと5,000円ほどの値段で、高価ではないRaspberry Piですが、昨年(平成27年(2015))11月に流れたニュースでは、なんと650円(5ドル)という驚くべき価格の製品「Raspberry Pi Zero」もラインナップに投入されました。

Raspberry Pi Zero

 この値段と大きさでLinuxが走るのですから、驚きです。さすがにEthernetはついていませんが、USBの無線LANドングルを接続すればネットにつながります。

 「Raspberry Pi Zero」は、この値段と大きさのゆえに数量を稼ぐことができますから、IoTにからむ何らかのブレイク・スルーをもたらす可能性も相当にあると言えるでしょう。

4 むすび

 私などが若い頃には、ワンボード・コンピュータというとNECのTK-80にとどめを打ったものです。一定の年齢層の人には大変懐かしいものの一つです。

 他方、ここまで触れましたように、現在のワンボード・コンピュータは長足の進歩を遂げており、ますます興味の尽きないものに変貌しております。

 今回紹介したものは一般向けのものなので、エンタープライズでの応用については、別途考察と検討、研究が必要であると思われますが、既に一般向けには広く普及していることから、早晩業務用途にも応用が広がることが想像されます。

耽るLチカ

投稿日:

 LEDは半導体であるから、電球と違って、光り始める前は抵抗は無限大であり、電流は流れない。ところが電圧がVfを超えて光り始めたら最後、抵抗がなくなって電源をショートさせた状態でぶっ飛び、自分自身も壊れる。従って必ず抵抗を一緒にくっつけてやるのだ。

 その抵抗を選ぶのは簡単で、基本的に E = I\cdot R という、この式のみでよい。LEDを買ってくると、流すべき電流値(If)、光り始める電圧(Vf)がどこかに書いてあるから、それに従って計算する。すなわち、

R = \frac{V - Vf}{If}

ここに、

V 自分が用意する電源の電圧
Vf 買ってきたLEDのVf(順方向電圧)、つまり光り始める電圧
If 買ってきたLEDのIf(順方向電流)、つまり光らせるために必要な電流

 ただ、抵抗は入手可能な数値が決まっており、そのものピタリという抵抗値のものは売っていない。なので、計算した値と一番近い抵抗を選び、その抵抗値で再び電流を計算して、買ってきたLEDのIfを超えていないかどうかを確かめる。

 これが、簡単な計算ではあるけどいちいち面倒くさい。

 それでまあ、抵抗を選ぶのにこういうスプレッドシートを作る。

 それから、これを使ってみよう。

IMG_3181

 これは、「武蔵野電波のプロトタイパーズ第15回『TLC5940で16個のLEDを遊ぶ』」で取り上げられている「TLC5940NT」というICだ。千石電商本店2階、入って左側の、一番奥のほうの抽斗で売られている。武蔵野電波のページでは400~700円とされているが、千石電商の店頭売りでは390円である。

 Arduinoで使うには、GitHubにあるライブラリをダウンロードし、zipを展開して出てくる「tlc5940」というディレクトリをArduino IDEのインストールディレクトリの下にある「libraries」の中にコピーすればよい。

 スペックシートはコレだが、スペックシートを見るより、Arduinoにライブラリを入れると出てくるようになるサンプルスケッチの「BasicUse」を見た方が分かり易いと思う。こんな風にサンプルは書かれている。

/*
    Basic Pin setup:
    ------------                                  ---u----
    ARDUINO   13|-> SCLK (pin 25)           OUT1 |1     28| OUT channel 0
              12|                           OUT2 |2     27|-> GND (VPRG)
              11|-> SIN (pin 26)            OUT3 |3     26|-> SIN (pin 11)
              10|-> BLANK (pin 23)          OUT4 |4     25|-> SCLK (pin 13)
               9|-> XLAT (pin 24)             .  |5     24|-> XLAT (pin 9)
               8|                             .  |6     23|-> BLANK (pin 10)
               7|                             .  |7     22|-> GND
               6|                             .  |8     21|-> VCC (+5V)
               5|                             .  |9     20|-> 2K Resistor -> GND
               4|                             .  |10    19|-> +5V (DCPRG)
               3|-> GSCLK (pin 18)            .  |11    18|-> GSCLK (pin 3)
               2|                             .  |12    17|-> SOUT
               1|                             .  |13    16|-> XERR
               0|                           OUT14|14    15| OUT channel 15
    ------------                                  --------

    -  Put the longer leg (anode) of the LEDs in the +5V and the shorter leg
         (cathode) in OUT(0-15).
    -  +5V from Arduino -> TLC pin 21 and 19     (VCC and DCPRG)
    -  GND from Arduino -> TLC pin 22 and 27     (GND and VPRG)
    -  digital 3        -> TLC pin 18            (GSCLK)
    -  digital 9        -> TLC pin 24            (XLAT)
    -  digital 10       -> TLC pin 23            (BLANK)
    -  digital 11       -> TLC pin 26            (SIN)
    -  digital 13       -> TLC pin 25            (SCLK)
    -  The 2K resistor between TLC pin 20 and GND will let ~20mA through each
       LED.  To be precise, it's I = 39.06 / R (in ohms).  This doesn't depend
       on the LED driving voltage.
    - (Optional): put a pull-up resistor (~10k) between +5V and BLANK so that
                  all the LEDs will turn off when the Arduino is reset.

    If you are daisy-chaining more than one TLC, connect the SOUT of the first
    TLC to the SIN of the next.  All the other pins should just be connected
    together:
        BLANK on Arduino -> BLANK of TLC1 -> BLANK of TLC2 -> ...
        XLAT on Arduino  -> XLAT of TLC1  -> XLAT of TLC2  -> ...
    The one exception is that each TLC needs it's own resistor between pin 20
    and GND.

    This library uses the PWM output ability of digital pins 3, 9, 10, and 11.
    Do not use analogWrite(...) on these pins.

    This sketch does the Knight Rider strobe across a line of LEDs.

    Alex Leone <acleone ~AT~ gmail.com>, 2009-02-03 */

#include "Tlc5940.h"

void setup()
{
  /* Call Tlc.init() to setup the tlc.
     You can optionally pass an initial PWM value (0 - 4095) for all channels.*/
  Tlc.init();
}

/* This loop will create a Knight Rider-like effect if you have LEDs plugged
   into all the TLC outputs.  NUM_TLCS is defined in "tlc_config.h" in the
   library folder.  After editing tlc_config.h for your setup, delete the
   Tlc5940.o file to save the changes. */

void loop()
{
  int direction = 1;
  for (int channel = 0; channel < NUM_TLCS * 16; channel += direction) {

    /* Tlc.clear() sets all the grayscale values to zero, but does not send
       them to the TLCs.  To actually send the data, call Tlc.update() */
    Tlc.clear();

    /* Tlc.set(channel (0-15), value (0-4095)) sets the grayscale value for
       one channel (15 is OUT15 on the first TLC, if multiple TLCs are daisy-
       chained, then channel = 16 would be OUT0 of the second TLC, etc.).

       value goes from off (0) to always on (4095).

       Like Tlc.clear(), this function only sets up the data, Tlc.update()
       will send the data. */
    if (channel == 0) {
      direction = 1;
    } else {
      Tlc.set(channel - 1, 1000);
    }
    Tlc.set(channel, 4095);
    if (channel != NUM_TLCS * 16 - 1) {
      Tlc.set(channel + 1, 1000);
    } else {
      direction = -1;
    }

    /* Tlc.update() sends the data to the TLCs.  This is when the LEDs will
       actually change. */
    Tlc.update();

    delay(75);
  }

}

 この最初のほうのコメントにアスキー・アートで書かれている図を見て結線するとよい。こんな感じだ。

IMG_3185

 LEDは秋葉原・千石電商の隣の店、「akiba LEDピカリ館」で売っていた10個入り300円の白色LEDで、Vfが3.0V~3.4V、Ifが20mAとある。電源が5Vならば100Ωばかり抵抗を付けてやればいい理屈だが、全部点灯させるとArduinoがダメになってしまうから、さらに絞って10KΩつけてやる。

 コンパイルして動かすとこうなる。

 10kΩでもこれくらい明るい。

 このICを使うと、パルス幅変調の幅も、Arduinoが256段階であるのに比べ、4096段階と格段に細かくなる。

 アレンジを加えてみよう。昨日買ってきたポテンショメータを使う。アナログの4番ピンと5番ピンに50kΩのポテンショメータと10kΩの抵抗をつなぎ、それぞれを強さと速さにして、「尾を引いたみたいに」明るさ制御をする。

 ポテンショメータの回路はこうする。

IMG_3191

 図の「E1」をアナログ入力で読めばよい。ポテンショメータのつまみの位置は、次の計算でR2を求めれば明らかになる。

E_{0} = I_{0}\cdot(R_{1} + R_{2})
I_{0} = \frac{E_{0}}{R_{1} + R_{2}}…①
I_{0} = \frac{E_{1}}{R_{2}}…②
① = ②
\frac{E_{0}}{R_{1} + R_{2}} = \frac{E_{1}}{R_{2}}
E_{0}\cdot R_{2} = E_{1}\cdot R_{1} + E_{1}\cdot R_{2}
R_{2}(E_{0} - E_{1}) = E_{1}\cdot R_{1}
R_{2} = \frac{E_{1}\cdot R_{1}}{E_{0}-E_{1}}

 組み付けるとこうなる。

IMG_3187

 動かすとこんな感じ。

 スケッチはこんな感じ。

//
//  wPotentio2tlc5940.ino
//    ポテンショメータとTLC5940でLチカ
//    27.08.02(日)0900~
//    佐藤俊夫
//
#include "Tlc5940.h"
//
const float
  R1 = 10000.0,     //  ポテンショメータ前の抵抗10kΩ, 
  E0 = 5.0,         //  電源電圧5V, 
  MAXVR = 50000.0;  //  ポテンショメータの最大抵抗
const unsigned int   VR1 = 4, VR2 = 5;  //  ポテンショメータはアナログピンのA4・A5
const unsigned int MAX_LED = 15;  //  LEDは0~15の16個
//
void setup()
{
  Tlc.init();
  pinMode(VR1, INPUT);
  pinMode(VR2, INPUT);
}

void loop()
{
  float vr1 = 0.0, vr2 = 0.0, e11 = 0.0, e21 = 0.0;
  static unsigned int topLed = 0, tailLen = 10;
  e11 = analogRead(VR1) * (5.0 / 1024);
  e21 = analogRead(VR2) * (5.0 / 1024);
  vr1 = (e11 * R1) / (E0 - e11);  //  明るさ
  vr2 = (e21 * R1) / (E0 - e21);  //  速さ
  if(++topLed > MAX_LED + tailLen)  topLed = 0;
  Tlc.clear();
  int bright = constrain(fmap(vr1, 0.0, MAXVR, 0, 4095), 0, 4095);
  Tlc.set(topLed, bright);
  for(int i = topLed - 1; i >= 0; i--){
    bright -= (4096 / tailLen);
    if(bright < 0) bright = 0;
    Tlc.set(i, bright);
  }
  Tlc.update();
  unsigned int delayTime = constrain(fmap(vr2, 0.0, MAXVR, 100, 10), 10, 100);
  delay(delayTime);
}
//
float fmap(float x, float in_min, float in_max, float out_min, float out_max) {
  //  もともとの「map()」がlong int型でこの用途に合わないので、float型を定義
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

空の写真リベンジ

投稿日:

 先週試したAdafruit製の「小型TTLシリアルjpegカメラ」での間欠撮影先週はどうしたわけか同じ画像ばかり撮れてしまい、失敗である。

 (チナミに、このカメラのメーカーの「Adafruit」という会社、有名なマッシモ・バンジのTEDの中で紹介されていたことに気付いた。)

https://youtube.com/watch?v=UoBUXOOdLXY%3Ft%3D5m50s

 気を取り直して、スケッチを直す。撮影時のカメラのステータスを確認し、撮れていなければ何回でも撮り続ける。

「while(撮れてない)撮る;」

……というわけだ。まあ、万が一ハードウェアエラーなどがあるとループが回り続けるので良くないが、ループが回り続けようが結局のところはモノとしては電源を入れ直す他にどうしようもないので、こんなものだろう。

 それと、撮影が終わったらカメラをそのつどリセットすることにした。また、既に存在するファイル名は避けるようにした。こういう時、Arduinoには書式文字列付きの「sprintf」がないので、少し不便だなと思う。

 夜明けから日没までの一日の空の雲を撮りたいので、天気予報を見て雨が降らぬ確信を持ってから、昨夜寝る前にベランダにカメラをセットしておいた。朝早く起きるより楽だからだ。夜のうちに1000枚くらい写真が撮れてしまうが、SDカードには余裕があるので大丈夫である。

 昼間は用事があるのでカメラの面倒は見れないが、放置しておけば淡々と写真は撮れていく。

IMG_3143

 で、撮れた写真をWindows Movie Makerに流し込むと、微速撮影動画の一丁上がりだ。

 スケッチは次のとおりである。

//
//  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";
  while(!CAM.takePicture());
  do{
    filename[8]  = '0' + pnum / 1000;
    filename[9]  = '0' + (pnum / 100) % 10;
    filename[10] = '0' + (pnum /  10) % 10;
    filename[11] = '0' + pnum % 10;
    pnum ++;
  }while(SD.exists(filename));
  if(pnum > 9999) pnum = 0;
  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;
  }
  while(!CAM.reset());
  imgFile.close();
}




赤外線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 ブレッドボードは結局、最後はこんな感じ。

カメラで遊んでみる

投稿日:

 秋月電子のサイトを見ていると、小さなカメラがあり、Arduinoに付きそうな感じだ。

 3850円。Arduino自体が2800円かそこらなので、それに比べるとちょっと高いが、早速行って購入。

 だが、あまり情報は多くない。まず、メーカーのサイトを見ていくと、チュートリアルがあり、「とりあえずテストするには、電源をくれてやって、一番端のピンをテレビにつなぎゃあ絵が出る」みたいなザックリ感満載の解説が。それで、テレビにつなぐためのRCAジャックなども買う。

 チュートリアルはこれを読んでおけばだいたいいいようだ。

 ほどいてみるとこんな感じで、かなり小さい。

IMG_3112

 ピンのピッチが2mmで、ブレッドボードで扱いにくい。それで、普通の2.54mmのピンヘッダを出して、その根元をこんなふうにムリヤリ(笑)2mmピッチにせばめる。

IMG_3116

 こいつをカメラの基盤にえいやっ、とねじ込み、半田付けする。

IMG_3118

 なかなか小さいので、ルーペと老眼鏡を併用しつつ、ICなんか壊しちゃってもナンだから、20Wのぬるくて細い半田鏝でさっさとつける。

IMG_3117

 我ながらなかなかスピーディな仕事だなあ(笑)。

 で、メーカーのサイトには「5V」と書いてあるが、これは互換品の別の製品のためのチュートリアルのようで、買ってきたものの基盤をよく見ると「3.3V」と印刷されている。壊してはもったいないから、3.3Vで試す。3.3Vの電源代わりにArduinoの3.3Vピンを使う。

 基盤の印刷通り、3.3V、GND、それから右端のピンをRCAジャックのセンターに、RCAジャックのアースを同じくGNDに入れて、テレビの前に持っていく。

IMG_3119

 テレビにつなぐと、おお、確かに、値段なりのフザけた画質(笑)で、自分の顔が映る。

IMG_3121

 上下が逆だが、まあ、いいや。

 で、今度は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のサイトのチュートリアルを参考に回路をブレッドボードに組む。

IMG_3122

 注意する点は2つだ。

  1.  サンプルスケッチをよく読むと、普通のSDカードは10番ピンを使うが、ETHERNET SHIELD 2を使う場合は4番ピンにつながる。なので、サンプルスケッチの中の「#define chipSelect 10」というところを「#define chipSelect 4」に書き換えなければならない。
  2.  メーカーサイトのチュートリアルでは、カメラに添付の10kΩの抵抗をTXの次に直列に二つ入れて、1本目と2本目の間からTXをとり、それをアースしているが、どうもこれだとうまく行かなかった。多分、このチュートリアルは給電が5Vだからだと思う。そこで、アースはそのままに、1本目の手前でTXをとるとうまくいった。
    IMG_3127

 そうやってArduinoをスタートさせると、写真が1枚だけ撮れる。

 下は、そうやって撮った私の顔である。

IMAGE02

 ……むっちゃむさくるしいなあw。