Raspberry Pi 2 Model B に WiFi、AD変換ICでアナログ入力

投稿日:
とりあえず無線LANにする

 Raspberry Pi、線がダラダラつながっているのは面倒くさいから、メインのifをWiFiにしてみたい。たまたま、Raspberry Piと相性がいいと言われるUSBのWiFiアダプタ「PLANEX GW-USNano2」というのをかなり前から持っている。

 USBポートにとりあえずこれを挿入し、

# lsusb
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 004: ID 2019:ab2a PLANEX GW-USNano2 802.11n Wireless Adapter [Realtek RTL8188CUS]

……もう、サクッとプラグアンドプレイの一発認識である。

# ifconfig
eth0      Link encap:イーサネット  ハードウェアアドレス b8:27:eb:45:1d:d0
          inetアドレス:192.168.1.105 ブロードキャスト:192.168.1.255  マスク:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  メトリック:1
          RXパケット:498 エラー:0 損失:1 オーバラン:0 フレーム:0
          TXパケット:253 エラー:0 損失:0 オーバラン:0 キャリア:0
      衝突(Collisions):0 TXキュー長:1000
          RXバイト:46665 (45.5 KiB)  TXバイト:34011 (33.2 KiB)

lo        Link encap:ローカルループバック
          inetアドレス:127.0.0.1 マスク:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  メトリック:1
          RXパケット:8 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:8 エラー:0 損失:0 オーバラン:0 キャリア:0
      衝突(Collisions):0 TXキュー長:0
          RXバイト:1104 (1.0 KiB)  TXバイト:1104 (1.0 KiB)

wlan0     Link encap:イーサネット  ハードウェアアドレス 00:22:cf:97:fe:b9
          inetアドレス:192.168.1.107 ブロードキャスト:192.168.1.255  マスク:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  メトリック:1
          RXパケット:506 エラー:0 損失:169 オーバラン:0 フレーム:0
          TXパケット:25 エラー:0 損失:0 オーバラン:0 キャリア:0
      衝突(Collisions):0 TXキュー長:1000
          RXバイト:128714 (125.6 KiB)  TXバイト:4541 (4.4 KiB)

 これも、もう、イッパツ。

 で、# shutdown -i0 -g0 -y、LANケーブル抜いて電源を入れなおす。

 サクッとつながる。ケーブルがだらだらつながっていたのが減って、これはとてもいいなあ。

USBのWiFiアダプタが快調に作動している様子
IMG_3308

アナログ入力をしてみる

 さて、今度はいろいろといじってみよう。

 Raspberry PiとArduinoにはいろいろと違いがある。その違い認識は世間ではもうとうの昔に確定済みであるようだが、私はその無視できない一つが「アナログ入力のあるなしではないだろうか?」と思い至っている。

 Arduinoはシンプルで扱いやすいにもかかわらず、10ビットのアナログ入力が6個もあり、これは意外と使い応えがある。抵抗やポテンショメータ、サーミスタ、CDSセル、音声など、実にいろいろなものをつなぐことができるのだ。

 だが、Raspberry Piにはアナログ入力は、ない。

 アナログ入力をしたければ、手段の一つに「Arduinoをつなぐ」というのがある。Arduinoを持っているなら、これは良い手段だが、なんだか無駄が大きい。

 もう一つが、「A/D変換器をつなぐ」ということだ。A/D変換IC、つまり「ADC」は、百何十円~数百円だから安い。合理的である。

 これについて解説しておられるサイトはたくさんある。それを参考にさせていただき、早速秋葉原へ行って2種類ばかりICを買い込む。

 これは、マイクロチップ・テクノロジー社製の「MCP3008-I/P」。8チャンネル10ビット、千石電商で320円。

MCP3008-I/P
IMG_3309

 コッチは、同じくマイクロチップ・テクノロジー社の「MCP3208-CI/P」。こちらも8チャンネルだが、幅は12ビット。秋月電子でソケット付き、データシート付きで320円。

MCP3208-CI/P
IMG_3310

 ついでにこの前壊したTLC5940NTも二つ買い込む。これも390円。

TLC5940NT
IMG_3311

 で、まずはMCP3208から試してみよう。

 Raspberry PiとICは、SPI通信でつなぐ。そのため、まず、Raspberry PiでSPI通信を行う要領を知る必要がある。

 Raspberry Piの場合は、SPIのモジュールをインストールしたり、ブラックリストから外すなどの着意が必要であったようだが、Raspberry Pi 2 の場合は、どうやらそのような操作は必要がなく、「# raspi-config」でAdvancedの中からSPIを選び、enableにすればよい。

 lsmodで確認し、次のように「spi_bcm2835」というモジュールが入っていればSPIが使える。

$ lsmod
Module                  Size  Used by
cfg80211              420690  0
rfkill                 16659  1 cfg80211
snd_bcm2835            19769  0
snd_pcm                74825  1 snd_bcm2835
snd_seq                53561  0
snd_seq_device          3650  1 snd_seq
snd_timer              18157  2 snd_pcm,snd_seq
snd                    52116  5 snd_bcm2835,snd_timer,snd_pcm,snd_seq,snd_seq_device
8192cu                528485  0
spi_bcm2835             7100  0
i2c_bcm2708             5014  0
uio_pdrv_genirq         2966  0
uio                     8235  1 uio_pdrv_genirq

 さっそくブレッドボードにMCP3208を配置し宮城大学のサイトの記事にあるコードを使わせてもらったのだが、これがそのままでは動かず、ハマッた、ハマッた……。

ブレッドボードの状況
IMG_3312

 何か、Raspberry PiとRaspberry Pi 2 で、ioctrlに渡すデータ長に違いでもあるらしく、かなり試行錯誤した末、このようになった。

 宮城大学のサイトにあった元のコード(“raspSPI.cpp”内)

//  sendRecN: Nバイトデータの送信と受信(2048バイトまで)
void SPI::sendRecN (unsigned char *send, unsigned char *rec, int n)
{
    //  setup a block
    struct spi_ioc_transfer tr[1];
    tr[0].tx_buf = (unsigned int) send;
    tr[0].rx_buf = (unsigned int) rec;
    tr[0].len    = n;
    tr[0].speed_hz      = clock;
    tr[0].delay_usecs   = SPI_DELAY;
    tr[0].bits_per_word = SPI_BITS;
    tr[0].cs_change     = 0;
    //  send this byte
    int ret = ioctl(fd, SPI_IOC_MESSAGE(1), tr);
    if (ret < 0) {
        printf("error: cannot send spi message (SPI::sendRecN)\n");
        exit(-1);
    }
}

 Raspberry Pi model Bで動くように少し変更したコード(tr[]の個数と、tr[0]に渡す値のキャストを「int」から「unsigned long」に変更している)

//  sendRecN: Nバイトデータの送信と受信(2048バイトまで)
void SPI::sendRecN (unsigned char *send, unsigned char *rec, int n)
{
    //  setup a block
    struct spi_ioc_transfer tr[3];
    tr[0].tx_buf = (unsigned long) send;
    tr[0].rx_buf = (unsigned long) rec;
    tr[0].len    = n;
    tr[0].speed_hz      = clock;
    tr[0].delay_usecs   = SPI_DELAY;
    tr[0].bits_per_word = SPI_BITS;
    tr[0].cs_change     = 0;
    //  send this byte
    int ret = ioctl(fd, SPI_IOC_MESSAGE(1), tr);
    if (ret < 0) {
        printf("error: cannot send spi message (SPI::sendRecN)\n");
        exit(-1);
    }
}

(作動させた画面の様子)

  1132   867   827   829   833   838   877  2670
  1090   829   787   787   792   797   845  2668
  1028   768   725   721   724   732   796  2671
   929   678   631   623   625   636   737  2669
   791   534   471   444   429   435   572  2667
   408   172    94    54    29    53   281  2668
    84     0     0     0     0     0   111  2666
     0     0     0     0     0     0    20  2671
     0     0     0     0     0     0     0  2670
     0     0     0     0     0     0     0  2668
    19    29    34    39    41    36    27  2670
    87    93   101   110   114   107    81  2670
   173   173   183   195   201   186   146  2671
   274   276   300   327   347   339   276  2667
   614   609   650   692   727   698   552  2668
   948   901   907   902   911   916   741  2672
  1102   916   878   886   894   899   854  2670
  1160   888   865   868   873   878   913  2671
  1137   872   832   833   838   843   903  2665
  1093   830   788   787   792   796   869  2668
  1027   767   723   717   721   727   815  2669
   938   685   638   631   632   643   748  2669
   821   566   504   482   467   474   603  2665
   447   206   128    89    62    85   303  2669
   107     0     0     0     0     0   123  2670
     0     0     0     0     0     0    32  2669
     0     0     0     0     0     0     0  2667
     0     0     0     0     0     0     0  2670
    14    23    29    34    37    32    29  2669
    81    86    94   102   104    98    80  2665
   170   167   177   186   191   174   141  2671
   267   267   290   314   333   323   263  2668
   598   593   631   669   702   672   536  2670
   936   888   908   904   913   914   723  2669
  1098   917   876   888   894   899   840  2668
  1157   895   864   869   875   878   904  2668
  1138   873   834   835   841   844   902  2667

 7番ピンにポテンショメータをつなぎ、残りのピンは解放してあるので、乱雑な値が出ている。

壊れちゃった

投稿日:

 先日作成した「ピアノでLチカ」の、光り方のダイナミックレンジと言うか、そういうのが小さいので、もっと多段階に光らせられないものかな、と思った。

 そもそも、5V/10ビット=1024段のAD変換器にせいぜい4ビット分くらいの入力しかしていないのだから、これを12ビット分4096に展開したって、光り方に濃淡が足りないのは当たり前である。

 これは適切なアンプを使っていないからだというのは自明だ。

 そこで、今朝、フリップフロップ遊びをするために使ったトランジスタ、「2SC1815」に目が向く。うんうん、これで1石増幅してやろう。

 普通はこの場面ではオペアンプとかリニアICを買いに行くのだろうが、何しろ150円で10個も入っていた2SC1815がもったいない。活用しなければ。

 えーっと、どうやるんだっけな、と思い出し思い出し、ブレッドボードを作る。

 抵抗分圧でベースにバイアスをかける。エミッタにつなぐ抵抗に増幅したい倍率をかけ、それをコレクタにつなぐ。普通は直流成分カットのためのコンデンサを出力につなぐが、0~5VまでのArduinoのアナログ入力に入れるのだから、これはいらない。

 結局、ベースバイアスには220kΩ×2個、コレクタに10kΩ、エミッタに1kΩをつけた。望ましい抵抗値は他にあると思うのだが、なにしろ、手持ちのものがそれしかない。

粗製1石アンプ
2SC1815アンプ

 いやまあ、お詳しい方からすると、各種の数値がツッコミどころ満載なのでしょうが、いや、その、手元にある抵抗等がこんなのしかなかったんですよ(苦笑)。

IMG_3305

 小さいスピーカーをつないで音を聴いてみると、耳がおかしくなるくらい音が歪んでいるが(笑)、レベルは十分であるようだ。

 こんなスケッチを書き、PCのヘッドホン端子にArduinoをつないでレベルをチェックする。

#include <stdio.h>
void setup() {
  Serial.begin(9600);
  pinMode(0, INPUT);
}

void loop() {
  String graph = "";
  char s[30];
  int v = analogRead(0);
  sprintf(s, "%4d ", v);
  graph.concat(s);
  for(int i = 0; i <= v / 10; i++){
    graph.concat("*");
  }
  Serial.println(graph);
}

 音声を直接Arduinoに入れるとこんなものだが……

音声直接入力の場合
増幅前

「2SC1815」を1個通しただけで、うんと幅広になる。

2SC1815で増幅して入力した場合
増幅後

……というわけで、うまく幅広な入力が得られるようになった。

 で、入力はこれでOKだ、というわけで、それを「ピアノLチカ」につないでみたのだが、あれ……?

 3端子レギュレータが触れないくらい熱くなっている。いかんいかん、というのですぐ電源を外す。回路にどこか間違いがあるのかな、と小一時間調べたが、間違いはない。

 どうやらLEDドライブICのTLC5940NTがダメになってしまったみたいだ。いつもArduinoを保護するために、TLCには3端子レギュレータで5Vの別建て電源を作り、それを与えていたのだが、ネットの情報によると、それでTLCが不安定な状態の時に電源が通り、壊れてしまうことはよくあることらしい。

 うーん、390円、もったいないなあ……。まあ、また買いに行くとするか……。ピアノLチカは、また今度だな……。

ノクターンでLチカ

投稿日:

 いやもう、「オッサンとバイエル、ピアノ等」って銘打ってて、長らくなかったこのネタ。ええ、ピアノですよ、ピアノ。

「Arduinoをピアノにつなぎたいッ!」

……すなわちコレである。Arduinoをピアノにつなぎ、LEDをピアノ演奏に合わせて明滅させようというものだ。

 いまや遅しとやってみた。

ノクターンでLチカ

 一つ覚えみたいにノクターンで申し訳ないですが(笑)、すんませんレパートリーがあんまりないもんで。

IMG_3276

 アナログピンからピアノの音声出力を読み、それに合わせてTLC5940NTに接続されたLEDを適宜明滅させるわけである。いかにもピアノ風味に明滅させるにあたっては、乱数によるLED選択と、先日会得した「PWMを十分に使い、余韻をもって各LEDを徐々に消す」という技を遺憾なく使用した。

 これをやるには、アナログピンに上手に音声を入れる必要がある。

 こういう時、電圧レベルを知るのにオシロスコープなどが使えればいいのだが、残念ながら持っていない。そこで、あらかじめテスターのACでテレビ、ラジオ、自分のピアノなどの音声出力端子からだいたいのレベルを推定し、次に十分な抵抗を入れて、Arduinoのアナログピンに入力して読み取った。その結果、だいたい200mVの振幅程度であることがわかった。これならばArduinoに直接入れてもどうということはなさそうだ。ただし、レベルが小さいから、ソフトウェアのほうでうまくやる必要がある。

 次に、音声はアナログ信号だから、プラス・マイナスに振れる。一方、Arduinoのアナログ入力は0~5Vの間を1024分割で読むのだから、ここをうまく工夫しなければならない。

 まず、2.5Vを中心に電圧が振れるよう、2.5Vを生成して合成する。ちょうどTLC5940NTを動かすために5Vの3端子レギュレータを使っているので、ここから抵抗分圧で2.5Vを作り、それを合成する。合成するとき、出力側の音声端子に電流が流れ込んではまずいから、コンデンサを0.1μFばかりつける。周波数は見当がつくが、電圧の見当がつきにくいので、とりあえず0.1μFにして、出来が悪いようなら取り換えることにする。

 まず、大して疑問もない簡素な付加回路。たしか、パルス技術ではこういうの、「クランパ」って言ったっけな……。

音声入力のためのクランパ
「voice2tlc」の回路図

 それで、TLCなどと一緒にLEDを植え込む。

IMG_3273

 ミニジャックを接続するために、秋月電子の「ステレオミニジャックDIP化キット」を取り付けた。

ミニジャックの部分
IMG_3274

 で、スケッチはこうなった。

//
//  voice2tlc.ino
//    アナログピンに音声を入れ、TLC5940NTを使ってLEDを光らせる。
//    27.08.13(木)1000~
//    佐藤俊夫
//
#include "Tlc5940.h"
#include <stdio.h>
//
const int
  AUDIO = 0, //  アナログ0ピン
  THRESH = 8;  //  8単位以上のレベルならLEDを点灯
//
void setup() {
  Tlc.init();
  Tlc.clear();
  Tlc.update();
}

void loop() {
  int v = 0, l = 0;
  static long int prevtime = 0.0;
  static int prevv = 0;
  v = abs(analogRead(AUDIO) - 512);
  l = constrain(fmap(v, 0, 15, 0, 4095), 0, 4095);
  if(millis() >= prevtime + 10){
    prevtime = millis();
    for(int i = 0; i <= 15; i++){
      Tlc.set(i, Tlc.get(i) * 0.9);
    }
  }
  for(int i = 0; i <= 15; i++){
    if(random(0, 16) == 1 && prevv != v && v >= THRESH){
      Tlc.set(i, l);
    }
  }
  Tlc.update();
  prevv = v;
}
//
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;
}

お稽古2

投稿日:

 7セグLEDの稽古をする。

 先日の稽古では、TLC5940NTで7セグLEDに数字を表示させる方法はわかったが、いまいちブレッドボードが乱雑で汚かった。

乱雑で汚いブレッドボード
IMG_3240

 そこで、なんとかこれを美しくする配置があるはずだ、と脳漿を絞った。

 いろいろとやったが、結局、「LEDの一番若いピンをTLC5940NTの一番若いピンに、残りのピンも同様に順番に」というような「論理的に美しい配置」だと、「物理的に汚く」なることがわかった。

 そこで、論理上のピン配置は多少前後しても、とにかく作動し、逆に物理的にはシンプルで美しく、誤りも見つけやすいという配置を追及したところ、このような配置・配線になった。

できる限り美しくした
IMG_3264

 なかなかシンメトリックで綺麗な配置になったと思う。誤りも見つけやすい。そのかわり、LEDの1・2・3・4番はそれぞれTLCの1・0・7・6番、……と言った具合に、ピンとピンとの対照関係は複雑になった。これはソフトウェアで吸収することになる。配列などでマッピングすると良いのだろう。

とにかく目一杯

投稿日:

 実用的なモノというとネット便器ぐらいしか作らず、Lチカばかりやっている今日この頃であるが、Lチカは楽しいので仕方がない。というか、「ネット便器のどこが実用的なのか?」というツッコミに極めて脆弱な文章をついつい書いてしまっているところもなかなか痛いが、さておき、昼間の「亀の子ブレボー」に、いつぞや大量生産したビーズ付きLEDを目一杯大量に植え込み、TLC5940NTでゴンゴンドライブしてみた。

 前回TLC5940NTを使ったときは、単純に右から左へ光るだけだったが、今度と言う今度は、もう、今思いつくだけあらん限りいろんな光らせ方をさせてみた。

 亀の子ブレッドボードは、なかなか省スペースで、うまく組み付けられる。

TLC5940NTを亀の子ブレボー(笑)に設置した状況
IMG_3259

 LEDをタップリ植え込むとこうなる。

IMG_3261

 動かすとこんな感じだ。

 スケッチはこうなった。はじめはタクトスイッチで手動切り替えをしていたが、面倒臭くなり、乱数で切り替えるようにした。

//
//  tlcVariation.ino
//    TLC5940NTにつないだLED、ランダムにいろんな光らせ方をしてみる。
//    27.08.11(火) 1515~
//    佐藤俊夫
//
#include "Tlc5940.h"
//
const int switchInterval = 5000;
//
void setup() {
  Tlc.init();
  Tlc.clear();
  Tlc.update();
}
//
void loop() {
  static int mode = 0;
  static long int prevtime = 0;
  if(millis() > prevtime + switchInterval){
    mode = random(0, 8);
    prevtime = millis();
    //  今のエフェクトを徐々に消す。
    int maxlum = 0;
    do{
      maxlum = 0;
      for(int i = 0; i <= 15; i++){
        Tlc.set(i, Tlc.get(i) * 0.9);
        maxlum = Tlc.get(i) > maxlum ? Tlc.get(i) : maxlum;
      }
      Tlc.update();
      delay(20);
    }while(maxlum > 0);
  }
  switch(mode){
    case 0:
      sinCurve_diff();
      break;
    case 1:
      inOrder_left();
      break;
    case 2:
      toCenter();
      break;
    case 3:
      inOrder_tail();
      break;
    case 4:
      left_right();
      break;
    case 5:
      left_right_tail();
      break;
    case 6:
      allAtOnce_tail();
      break;
    case 7:
      inOrder_right();
      break;
    default:
      sinCurve_diff();
      break;
  }
}
//
void inOrder_right(){
  //  0.1秒おきに右から切り替えていく。
  static long int prevtime = 0.0;
  static int led = 15;
  Tlc.clear();
  if(millis() >= prevtime + 100){
    Tlc.clear();
    Tlc.set(led--, 4095);
    led = led < 0 ? 15 : led;
    Tlc.update();
    prevtime = millis();
  }
}
//
void allAtOnce_tail(){
  //  一斉に点灯して徐々に消える。
  static long int prevtime = 0;
  if(millis() > prevtime + 100){
    prevtime = millis();
    if(Tlc.get(0) <= 0){
      for(int i = 0; i <= 15; i++){
        Tlc.set(i, 4095);
      }
    }else{
      for(int i = 0; i <= 15; i++){
        Tlc.set(i, Tlc.get(i) * 0.8);
      }
    }
    Tlc.update();
  }
}
    
//
void left_right_tail(){
  //  尾を引きながら右へ行ったり左へ行ったり。
  static long int prevtime = 0.0;
  static int led = 0, order = 1;
  if(millis() >= prevtime + 100){
    for(int i = 0; i <= 15; i++){
      Tlc.set(i, (int)((float)Tlc.get(i) * 0.5));
    }
    led = led + order;
    Tlc.set(led, 4095);
    order = (led >= 15) || (led <= 0) ? order * -1 : order;
    Tlc.update();
    prevtime = millis();
  }
}
//
void left_right(){
  //  右へ行ったり左へ行ったり
  static long int prevtime = 0.0;
  static int led = 0, order = 1;
  if(millis() >= prevtime + 100){
    Tlc.clear();
    led = led + order;
    Tlc.set(led, 4095);
    order = (led >= 15) || (led <= 0) ? order * -1 : order;
    Tlc.update();
    prevtime = millis();
  }
}
//
void inOrder_tail(){
  //  尾を引きながら0.1秒おきに左から切り替えていく。
  static long int prevtime = 0.0;
  static int led = 0;
  if(millis() >= prevtime + 100){
    for(int i = 0; i <= 15; i++){
      Tlc.set(i, (int)((float)Tlc.get(i) * 0.5));
    }
    Tlc.set(led++, 4095);
    led = led > 15 ? 0 : led;
    Tlc.update();
    prevtime = millis();
  }
}
//
void toCenter(){
  //  中央付近のLEDから外へ光らせる。
  static long int prevtime = 0.0;
  static int ledleft = 7, ledright = 8;
  if(millis() >= prevtime + 100){
    for(int i = 0; i <= 15; i++){
      Tlc.set(i, (int)((float)Tlc.get(i) * 0.5));
    }
    Tlc.set(ledright++, 4095);
    Tlc.set(ledleft--,  4095);
    ledright = ledright > 15 ? 8 : ledright;
    ledleft = ledleft < 0 ? 7 : ledleft;
    Tlc.update();
    prevtime = millis();
  }
  
}
//
void inOrder_left(){
  //  0.1秒おきに切り替えていく。
  static long int prevtime = 0.0;
  static int led = 0;
  Tlc.clear();
  if(millis() >= prevtime + 100){
    Tlc.clear();
    Tlc.set(led++, 4095);
    led = led > 15 ? 0 : led;
    Tlc.update();
    prevtime = millis();
  }
}
//
void sinCurve_diff(){
  //  呼ぶたびにサインカーブをちょっとづつずらしながら光らせる。
  static float x[16] = {
    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
  };
  for(int i = 0; i <= 15; i++){
    x[i] = x[i] >= (2.0 * PI) ? 0.0 : x[i] + ((2.0 * PI) / 1000.0) * (1.0 + (float)i / 10.0);
    Tlc.set(i, (int)(((sin(x[i]) + 1.0) / 2.0) * 4095));
  }
  Tlc.update();
}

2桁順番

投稿日:

 次に、TLC5940NTを使って、数字をちゃんと表示させる。

 前と同じ回路で、スケッチは次のようにする。

//
//  tlc594027segmentLED.ino
//    7セグメントLEDをTLC5940NTで制御
//    27.08.10(月)1800~
//    佐藤俊夫
//
#include "Tlc5940.h"
//
void setup()
{
  Tlc.init();
}

void loop()
{
  Tlc.clear();
  static int n = 0;
  disp7LED(n++);
  if(n >= 100) n = 0;
  delay(1000);
}
//
void disp7LED(int n){
  const int on = 4095, off = 0;
  const int pat[10][8] = {
    // 0    1    2    3    4    5    6    7pin   num
    { on,  on,  on, off,  on,  on,  on, off},  //  0
    {off, off,  on, off,  on, off, off, off},  //  1
    { on,  on, off, off,  on,  on, off,  on},  //  2
    {off,  on,  on, off,  on,  on, off,  on},  //  3
    {off, off,  on, off,  on, off,  on,  on},  //  4
    {off,  on,  on, off, off,  on,  on,  on},  //  5
    { on,  on,  on, off, off,  on,  on,  on},  //  6
    {off, off,  on, off,  on,  on,  on, off},  //  7
    { on,  on,  on, off,  on,  on,  on,  on},  //  8
    {off,  on,  on, off,  on,  on,  on,  on}   //  9
  };
  //  一旦消す
  Tlc.clear();
  //  10の位
  int n10 = n / 10, i = 0;
  for(i = 0; i <= 7; i++){
    Tlc.set(i, pat[n10][i]);
  }
  // 1の位
  int n1 = n % 10;
  for(i = 0; i <= 7; i++){
    Tlc.set(i + 8, pat[n1][i]);
  }
  Tlc.update();    
}

 次のようにカウントアップしていく。

 そういえば昔の刑事ドラマなんかで、時限爆弾のカウントって、こんな感じで、電線がゴチャゴチャしてたっけなあ。ふふっ、爆発しそうだな、これじゃ(笑)。

次に2桁を

投稿日:

 7セグメントLEDの1桁の制御の基礎がわかったので、今度は2桁だ。

 当然、ArduinoのI/Oはこれでは足りない。

 したがって、ドライバICを使う。この前買ったテキサス・インスツルメントの定番IC、「TLC5940NT」を使うのである。秋葉原千石電商で390円だ。

IMG_3240

 16本のカソードを全部ICに入れてあるので、配線はだいぶゴチャつく。ブレッドボードも2連結だ。

 この配線は、ArduinoのTLC5940用ライブラリをインストールすると使えるようになるサンプルスケッチ「BasicUse」のコメントの冒頭にアスキーアートで詳しく図示されている。

 とりあえず、この「BasicUse.ino」を動かしてみた。次のようになって面白い。

 

暑熱をばひとつ

投稿日:

 黙々と呟き続ける我が作品、「ネット便器」である。いや、便器そのものを作ったのはINAX社であるから、私の作品と言うこともあるまいが、まあ、こんな下品かつ無意味なものを作ることができるのもDIY、というかMAKERS精神なればこそ、である。

ツイートする私の作品「ネット便器」(笑)

 ちなみに、こういう手作りは、数年前からDIYとは言わず、Makersムーブメントと言うようになったのだそうな。DIYとの違いは、ざっくり言えばネットのあるなしである。

 それにしても、暑い。暑熱である。秋とは暦ばかり、なんて暑いんだ。

 私の家には温度計がないのだが、今日のような折ふし、たまさかには室温が知りたくもなる。知ったところで「うわっ32度だってよ余計に暑くなったゾなんてこったいッ!」などとうわ言のようにうそぶきつつ興奮する以外にないのだが、それでもやっぱり知りたいのである。

 エアコンのリモコンに設定温度とは別に温度計がついており、一応それで用は足りているのだが、1℃単位のザックリした温度計なので、不満である。

 こんなこともあろうかと、私こと佐藤は常々周到怠りない。我が作品「ネット便器」は、気温をツイートできるのだ!!トイレに行き、便器のフタを開け閉めしてから部屋に戻り、ツイッターを見ると、自宅の気温がだいたいわかるわけである。おお、なんとスンバラシイ。Arduino万歳。とっとと内紛やめて楽しくやろうぜベイベー!!

 ……。

 めんどくさい。

 だいたい、気温ぐらいその場でわかるようにすべきではなかったのか。便器のフタを開け閉めしてツイッター見なきゃ気温が分からん家なんて、どうなっとるんだ一体ッ!?。ネット便器の本体に気温を表示すべきだ!!っていうか、なんで便所で気温を測らねばならんのだ!!

 というわけで、発作的に自宅を飛び出し、向かったのは八潮の秋月電子である。

 「どうして近所の100円ショップで温度計を買わんのだ?」という愚問は禁止の方向でお願いしたい。

 秋葉原に行けばよいのだが、自宅からは八潮の秋月電子のほうが近いのである。それに、秋葉原の秋月電子は、人でごった返して足の踏み場がなく、店頭で品定めをする余裕が全くない。八潮の秋月電子は空いているので、店内でのんべんだらりとデータブックを読みながら部品を選ぶことも可能である。

 目当ては、日立「HD44780」という液晶ドライバの、互換ICを搭載した液晶ディスプレイである。大概の液晶ディスプレイは、この30年も前に開発された名作IC互換になっているのである。

 他に、7セグメントLEDで気温を表示させることも考えたが、実は思いのほか、Arduinoでの表示に限っては液晶ディスプレイのほうがラクなのだ。7セグメントLEDは簡素なだけに意外に奥が深く、多くの桁を表示させるためのダイナミック点灯やその明るさ補償、足りない電流を他の電源から持って来るなど、やることが多い。

 さて、秋月電子八潮店である。

 店内にはズラリと液晶ディスプレイが並んでいる。手ごろなところで、バックライト付きの液晶ディスプレイ、「SD1602 HUOB-XA」という型番のものを購入した。900円。

 他に、後で遊ぶためにアノードコモンの7セグメントLEDを買う。これは例の「TLC5940NT」に接続して遊ぶのである。ひとつ60円。

 それから、切らしてしまったQIコネクタのピン端子も買う。シースが見当たらないので、店員さんに「これのシースありませんか」と聞くと、ハウジングのことですか?この端子にはハウジングみたいなものはありませんよ、と答えるではないか。うーん。秋葉原の千石電商なら、左奥の抽斗にザクザク入っているのだが、どうも、八潮の秋月電子にはないらしい。というか、実は八潮の秋月の店員さん、QIコネクタにはシースがあるってことを知らんのではなかろうか。

 それはそれとして……。

今日買ったもの
IMG_3220

 帰宅して早速とりかかる。

 製品はこういうものがビニール袋に封入されているので、付属のピンヘッダを半田付けする必要がある。ちょいちょいちょい、と素早い仕事だ。

ピンヘッダをつける
IMG_3224
IMG_3225

ちょいちょいっとな、……っと。
IMG_3230

 ネットで情報を漁る。

 あるサイトによると、基盤ウラの「J3」というプリントをショートし、「R9」というプリントに100Ωの抵抗を付けると、基盤の電源でバックライトが光らせられる、とあるので、早速真似をする。

 ところが、他の回路とともに作動させてみると、どうも不安定である。バックライトの電流は、データシートによると40mAとある。電流を実回路で測定してみたところ、データシートに記載の値よりは少ないものの、37~38mAくらい流れていることが分かった。Arduinoで安心して流せるのは20mAまでなので、これはどうも過大かもしれない。Arduinoは50mAくらいまで流すことができるが、余裕は十分にあったほうがよいだろう、ということで、R9に取り付けた100Ωのジャンプ抵抗は取り外した。

 液晶ディスプレイのみ単体ならば余裕はあるものの、他の回路を接続するのであれば外部電源で点灯した方が良いように思われる。

 で、データシートと、Arduino IDEの「サンプルスケッチ」の中にある「LiquidCrystal」のコメントを参考にブレッドボードを結線する。ブレッドボードには「Seeedstudio SIDEKICK BASIC KIT」に入っていたサーミスタを、1kΩの抵抗とともに取り付けてアナログ1番ピンに入れる。

ブレッドボードの様子
IMG_3233

回路図
「暑熱をばひとつ」の回路図

 スケッチはこんなふうにゴリゴリと書いて、動けとばかりArduinoに注入し、荒い息を吐く。

//
//  thermistor2LCD.ino
//    サーミスタで気温を測り、LCDに表示する。
//    佐藤俊夫
//    27.08.09(日) 1900~
//
#include <stdio.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int THERMISTOR = 1;

void setup() {
  lcd.begin(16, 2);
  pinMode(THERMISTOR, INPUT);
}

void loop() {
  char tempStr[16], dispStr[32];
  lcd.setCursor(0, 0);
  dtostrf(tempMesure(), 5, 2, tempStr);
  sprintf(dispStr, "Temp. %s C", tempStr);
  lcd.print(dispStr);
  delay(500);
}
//
float tempMesure(){
  const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0;  //  MF11-503Kスペックシート記載
  const float K = 273.15;  //  熱力学温度の定数
  const float v0 = 5.0, r0 = 1000.0;  //  Arduino +5Vと電流調整抵抗1kΩ
  const int resolution = 1024;  //  アナログ入力の分解能
  int srcVal = 0;
  float vt = 0.0, rt = 0.0;
  
  srcVal = analogRead(THERMISTOR);
  vt = srcVal * (v0 / (resolution - 1));
  rt = (v0 * r0 - vt * r0) / vt;
  return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K);
}

 こうして、液晶ディスプレイに気温が表示できるようになった。

……ていうか、暑ッ!(笑)
IMG_3232



引き続きLチカ

投稿日:

 引き続きLEDで遊ぶ。

 100円ショップのプラスチック・ビーズにLEDを埋め込むという武蔵野電波のマネはなかなか楽しく、それをたくさん作ったのだが、この前のやり方だといまいち個数が少なく、寂しい感じが否めない。

IMG_3193
この前のLED生け花

 それはなぜかというと、小さいブレッドボード上に電流制御抵抗をLEDごとに付けようとすると、どうしてもLEDか抵抗のどちらかを「ラインまたぎ」に横向けにしなければならず、最低2ラインが消費されてしまうからだ。このブレッドボードだと、電源回路を入れると、どうしても5個しかLEDが配置できない。

どうしてもこうなる
IMG_3195

 ブレッドボードの中身は、タテのラインが下のように一列づつ内部で接続されており、真ん中の横一文字の仕切りで分けられている。抵抗かLEDのどちらかしか真ん中をまたげないから、どちらかは横向きになるのである。

ブレッドボードの中身の結線状況
ミニブレッドボード

 そこで、便利なものがある。「集合抵抗」だ。

集合抵抗
IMG_3203

 これは、抵抗を集めてワン・パッケージにしたものだ。この写真のものは、「8素子9ピン」というもので、内部はこうなっている。

集合抵抗の回路
集合抵抗の内部

 秋葉原千石電商なら、本店地下の、レジから一番遠い、奥の抽斗で売られている。ひとつ20円だ。

 表面にはカラーコードではなく、「103」等と数字が刻まれている。これはセラコンの読み方と似ていて、「103」であれば 10\times 10^3=10k\Omega である。今回は「331」、すなわち33\times 10^1=330\Omega のものを買ってきた。

 足のピッチはちょうど2.54mmなので、ブレッドボードにピッタリ挿すことができる。これを使うと、ブレッドボード上、LEDと抵抗の組み合わせで2ピッチ消費していたところを1ピッチですませることができる。

集合抵抗を使うと1ピッチですむ
IMG_3202

 こうすると、LEDは真ん中の仕切りをまたぐだけでよいから、LEDビーズの花を8個植えることができる。

IMG_3207

 紙コップに活けると、前回より多少華やかになった。

IMG_3200

 さて、LEDビーズが増えたので、これを16個ほど量産し、「TLC5940NT」とArduinoの回路に取り付けてみよう。スケッチやブレッドボードは前に試したのと同じでいい。

IMG_3212

 動かすとこんな感じだ。

 パルス幅変調がうまくかかって、1個1個のLEDの消え具合になかなか余韻があってよい。また、よく見ていただくと、赤いビーズが必ずしも赤く光るわけではなく、緑や青に変化して光るのも、面白いところだ。




耽る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;
}