暗い時にLEDが光るようにしたい

スポンサーリンク
スポンサーリンク

光センサー

光センサースイッチキットを買ったはいいものの、説明書見ても使い方がぜんぜんわからない。なので諦めて一から方法を検討してみる。どんな方法があるのかなーと調べてみたところ

  • フォトレジスタを使う
  • フォトトランジスタを使う
  • ↑+ADコンバータ使う
  • 照度センサ使う

みたいな方法があるそうで、光センサースイッチキットで使われてた、フォトレジスタを使う方法でいってみたい。

スポンサーリンク

フォトレジスタ、CdSセルを使ってみる

つーことでGL5528を買ってきた。テスターで抵抗値を測ってみると、日中の室内で4kΩ、手をかざすと15kΩ、指で隠すと100kΩぐらいだった。ちなみに光センサースイッチキットに入ってるCdSセル(多分これ)を同じように測ってみると、日中の室内で20kΩ、手をかざすと50kΩ、指で隠すと160kΩぐらいだった。えらいばらつきあんな…。

で、このままLチカした時の回路の抵抗の代わりにフォトレジスタ使ったとしても、暗い時に暗くなって、明るい時に明るくなるだけで。じゃあどうするかというと、分圧回路というのにするみたい。

Wikipediaより

こんな。で、R2の方にフォトレジスタを付けてあげればいいみたい。えっと、Vinは3.3V、R2は明るい時4kΩ、暗い時100kΩで、Voutは明るい時はLED点かないぐらいの電圧、暗い時は点くぐらいの電圧になってほしい。点くぐらいの電圧っていうのはつまり順方向電圧なので、Lピカした時の2.6Vになってくれればいいかな。ほいでR1の抵抗値を求める。

Vout = R2 / (R1 + R2) * Vin

なので、2.6 = (100000 / (x + 100000)) * 3.3を解く。Wolframalphaさんに頼むとR1≒27kΩとなった。ほんとかしら、参考サイトだと数kΩだったぞ。ついでにR1に27kΩ使った時、明るい時のVoutはどんななんだろと思いx = ( 4000 / (27000 + 4000)) * 3.3を計算してもらうと約0.43Vなので点かなそう、計算上はいい感じです。実際やってみてどうなるだろうか。

都合よく27kΩの抵抗があったので、それを挿して回路図通りになるようにし、フォトレジスタを指で隠してみると…、点かない。うーん。フォトレジスタの抵抗値が安定してないような感じ。最初測った時は100kΩだったけど、今測ると80kとかになる。うーん。とりあえず暗い時を80kとして計算し直すとR1は約22kΩなのでこれまた都合良くあった抵抗を挿し替えると

点いた!この時の電圧をテスターで測ってみると、LEDが2.39V、R1(22kΩ)が0.885V、R2(フォトレジスタ)が2.4Vだった。LEDとR2は並列なので電圧は一緒のはず、R1は電源電圧 – LED&R2の電圧のはずなので、大体合ってる。ここから、R1に流れる電流を求めると、0.885 / 22k = 約0.04mAの電流が流れてる。んで…あれ?LEDとR2のそれぞれに流れてる電流ってどうやって求めんの?えーと、フォトレジスタの抵抗値を80kΩと仮定してあげると、2.4 / 80k =  0.03mAでキルヒホッフの法則から0.04 – 0.03 = 0.01mAがLEDに流れてる…の?そんなんありえるんだろうか。計算間違ってないかな。うーん、まぁ点いちゃってるんだからしょうがないよな。

スポンサーリンク

フォトレジスタ+トランジスタで明るくする

LEDを明るくするには、流れ込む電流を多くしなくちゃいけないわけで、そういう時に使うのがトランジスタということみたい。ということで2SC1815買ってきた、20個もいんないんだけどな。トランジスタは3本足で、コレクタ・エミッタ・ベースと分かれてる。通常、コレクタ-エミッタ間は電気が流れてなくて、ベースに電気を流すと増幅された電気がコレクタ-エミッタ間に流れるみたい、買ったやつはNPN型だから。そん時に流れる電流の値がどんくらいになるかっていうのをhFE(直流電流増幅率)で表すそうな。hFE = Ic / Ibね、エミッタ接地の場合。買ったやつをテスタで測ってみたらhFE=284だった。ものすごい単純に考えたら0.04mA * 284 = 11.36mAになるってこと?こえーな、大丈夫かな。とりあえず、あまり考えず抵抗とか変えずに参考サイトの回路図の通りに繋いでみると

点くは点くんだけど、スマホのLEDライトが当たってるぐらいじゃないと明るいと判定されない。室内灯の明るさの元では暗いと判定されて光ってしまう。なんでだろ。今繋いだ回路図が

こんな。うーん、室内灯下でフォトレジスタの抵抗を測ってみたら10kΩほどだった。この時Voutはいくらか計算すると1.03V。あ、なるほど、トランジスタがONになっちゃってるんだ。トランジスタがONになるベース-エミッタ間の電圧は約0.7Vらしい、根拠解説してるとこ見つかんなかった。嘘です、あったけどよく分かんなかった(1, 2, 3)。なので、明るい時にVoutが0.7より小さくならないと駄目ってことみたい。とりあえず0.6を狙って計算してみるとR1が45kΩと出た。持ってる内で45kになる組み合わせないかなーと探すと18kΩと27kΩを直列にすれば良さげ。と思ってやってみたところ、

惜しい!光は弱まったけど、光っちゃった。ていうか0.6Vとかじゃなくてもっとガッツリ減らしたほうがいいみたい。なので0.3V狙いで計算すると100kΩとな。100kΩならあるぞ、やってみよう。

で、できたー。室内灯下でもLEDは点かず、カバーを被せて暗くしたら点くようになった。あとはこれをGPIOに繋げば、カメラで撮影する時にHIGHにするだけで、回路上で明るさを判定して暗い時はLEDを点けてくれるはず。

スポンサーリンク

ADコンバータを使ってみる

これまでの回路だと、明暗の閾値が分からないよね。薄暗い時にぼんやりと光る、みたいなことになりそう。で、閾値をいい感じに指定したいって場合に便利なのがADコンバータみたい。フォトレジスタの抵抗値をデジタルに変換してくれるので、数値での指定が可能になるってことみたい。てことでADコンバータのMCP3208を買った。これを解説してるサイトとかデータシートとかを見たのだけど、分かってる人がちょっと分かってる人に言ってる感じで、全然分かってない人にはちんぷんかんぷんな感じ。つまり俺にはちんぷんかんぷんです、ゲロ吐いて鼻血が出そう。そもそも使われてる単語がわからん、なんで3,4文字の略称ばっかなんだ。

用語を調べる

分かってる風に書いてるけどほぼ分かってない。

MCP3208

データシートより

PDIP ICチップの形状(パッケージ)の名前。Dual Inline Package。2側面からピンが出ててプラスチック。
SOIC ピンピッチが1.27mmのもの。Small Outline Integrated Circuit。
CH0~CH7 CHannel。アナログ値の入力で使う。0-7までの8本で別々の値が取得できる。
VDD 動作電圧。IC自体の電源電圧。Voltage Drain。
VREF 基準電圧。Voltage REFerence。ここに接続する電圧と、CH*に接続する電圧の比がデジタルの数値となるみたい。
AGND アナログ接地。Analog GrouND。
CLK クロック。CLocK。これ?
DOUT シリアルデータ出力。DはDataの略ですかね…
DIN シリアルデータ入力。
CS/SHDN Chip Select / SHutDowN。LOWで通信開始、HIGHで待機。
DGND デジタル接地。Degital GrouND。
12bit 2^12の分解能。基準電圧3.3Vの場合、3.3 / 4096 = 0.8mVごとの変化を読める。

Rasberry Pi GPIO

SPIで利用できるピンは2セットあるようで、物理ピン19,21,23(BCM10,09,11)と35,38,40(BCM19,20,21)を使う。あと物理ピン24,26(BCM8,7)でどっち使うか決める。物理ピン11,12もSPIに使えるみたいだけどよく分かってない。通常は物理ピン19,21,23,24を使えば良さげ。

SPI Serial Peripheral Interface。通信方式の一つ。
MOSI Master Out Slave In。ラズパイ→ICへデータを送る。ICのDINに繋ぐ。
MISO Master In Slave Out。IC→ラズパイへデータを送る。ICのDOUTに繋ぐ。
SCLK Serial CLocK。クロック…ICとラズパイの周期を合わせる感じで使われるみたいな。ICのCLKと繋ぐ。
CE0, CE1 Chip Enable。0と1で使いたいのを選ぶ。ICのCS/SHDNと繋ぐ。

その他

シングルエンド グラウンド(0V)を基準に信号の電圧レベルでLOWと HIGHが決まる信号。
差動 一つの信号当たり必ず2本の信号が使われる。二つの信号の電位差が信号レベルになる。例えば差がプラスであればHIGH、マイナスであればLOWのように認識する。
LSB 最下位ビット。Least Significant Bit。
MSB 最上位ビット。Most Significant Bit。
baud 変調速度単位。ボー。

配線

した。線がいっぱいで汚いなぁ。

SPI通信の手順

ラズパイ-ICはMaster-Slaveの関係にある、あ、SMだね。MasterのChipSelect(SlaveSelect)が複数あればICは複数繋げることができる。Masterが通信の制御を行う。

  1. CSは待機状態の場合HIGHになってる。
  2. 使用したいCSをLOWにすると通信が開始される。
  3. SCLKのL・Hを切り替えるタイミングでデータが1bitずつやり取りされる。
  4. Master→Slaveの場合MOSI、Slave→Masterの場合MISOが使われる。信号の順番は、例えば”10110010″ってデータの場合1→0→1→1→0→0→1→0の順でやり取りされる。
  5. やり取りが終わるとCSをHIGHにして待機状態に戻して終わり。

めちゃめちゃざっくりこんな感じみたい。

MCP3208とのデータのやり取り

頭の中がこんがらがってる。えーと、上記3のタイミングに合わせつつ、

  • Master→Slaveに開始ビット+制御ビットで計5bit送信する。開始ビットは1、制御ビットの構成は、シングルエンドor差動の選択で1bit、D2・D1・D0の3bitでCH0~CH7を選択する(下図)。
  • D0が送信し終わると、1.5クロックのアナログ入力サンプル時間が入りそこは不定ビットになる、その次のクロックからSlave→Masterにデータが13bit送信される。最初にnullの1bit、その次から12bit分がアナログ/デジタル変換したデータ。

ここまでのイメージ

クロック 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
MOSI(DIN) 開始 シ差 D2 D1 D0
MISO(DOUT) null B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0

で、これを8ビットごとに区切らないとならないみたい。理由は知りません。今全部で19bitなので8の倍数で、16ビットだと溢れちゃうので24ビットにする。ケツ合わせにするようで、先頭を0フィルする。これも理由は知りません。扱いやすくなるからみたいな話らしい。すると

クロック 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
MOSI(DIN) 0 0 0 0 0 開始 シ差 D2 D1 D0 x x x x x x x x x x x x x x
MISO(DOUT) ? ? ? ? ? ? ? ? ? ? ? null B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0

こうなる。xは0と1のどっちでもいい。?は不定。ほんとはクロックの下降部でデータを送るか上昇部でデータを送るかとか、クロックの基準はLowかHighかとか関係があるので、こういう表での書き方はあんまよくないみたい。まぁ、これを参考に、ラズパイのGPIOを操作するよう実装していく感じになる。俺にそこまでできるのだろうか。

データシートより

データシートより

データシートより

Rasberry Pi のSPIを有効化

$ sudo raspi-config

5 Interfacing Options→P4 SPI→Yes。

$ ls /dev/spi*

で、/dev/spidev0.0と/dev/spidev0.1が表示されればよい。

pigpioライブラリで実装

このライブラリ、web上の情報が他のライブラリに比べて少ない気がする…。見様見真似で書いてみる。できたpyファイルは$ sudo chmod で適切な権限付ける。

import pigpio
pi = pigpio.pi()
h = pi.spi_open(0, 50000, 0)

if not pi.connected:
   exit(0)

(count, rx_data) = pi.spi_xfer(h, [6, 0, 0])
print(count)
print(bin(rx_data[1]))
print(bin(rx_data[2]))

pi.spi_close(h)
pi.stop()

とりあえずこんな感じ。で実行。

3
0b0
0b0

0しか返ってこない…。なんでだー、見当がつかないな。エラーは起きてないのにな、チップがイカれてんのかな。なんだろうなんだろう、といろいろ試した結果

ICがブレッドボードにちゃんと刺さってませんでした。イカれてんのは俺のアタマでした。さぁ、ついでに

bi = rx_data[1] << 8 | rx_data[2]

print(bi)
print(bin(bi))

などと追記し、再度実行すると

3
0b1011
0b1001010
2890
0b101101001010

うひょーちゃんと出た!やったぜ!

4行目

spi_open(spi_channel, baud, spi_flags)でSPIの接続をする。

  • spi_channnel
    • 配線したCExに合わせて指定。0-2。
  • baud
    • 変換速度指定、単位bps。ここでは50kbps…なのかな。
  • spi_flags

spi_flags consists of the least significant 22 bits.

21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
b  b  b  b  b  b  R  T  n  n  n  n  W  A u2 u1 u0 p2 p1 p0  m  m

mm defines the SPI mode.

WARNING: modes 1 and 3 do not appear to work on the auxiliary SPI.

Mode POL PHA
0    0   0
1    0   1
2    1   0
3    1   1

px is 0 if CEx is active low (default) and 1 for active high.

ux is 0 if the CEx GPIO is reserved for SPI (default) and 1 otherwise.

A is 0 for the main SPI, 1 for the auxiliary SPI.

W is 0 if the device is not 3-wire, 1 if the device is 3-wire. Main SPI only.

nnnn defines the number of bytes (0-15) to write before switching the MOSI line to MISO to read data. This field is ignored if W is not set. Main SPI only.

T is 1 if the least significant bit is transmitted on MOSI first, the default (0) shifts the most significant bit out first. Auxiliary SPI only.

R is 1 if the least significant bit is received on MISO first, the default (0) receives the most significant bit first. Auxiliary SPI only.

bbbbbb defines the word size in bits (0-32). The default (0) sets 8 bits per word. Auxiliary SPI only.

The spi_readspi_write, and spi_xfer functions transfer data packed into 1, 2, or 4 bytes according to the word size in bits.

For bits 1-8 there will be one byte per character. For bits 9-16 there will be two bytes per character. For bits 17-32 there will be four bytes per character.

Multi-byte transfers are made in least significant byte first order.

E.g. to transfer 32 11-bit words data should contain 64 bytes.

E.g. to transfer the 14 bit value 0x1ABC send the bytes 0xBC followed by 0x1A.

The other bits in flags should be set to zero.

Example

# open SPI device on channel 1 in mode 3 at 50000 bits per second

h = pi.spi_open(1, 50000, 3)

色々設定。22bitを10進数に変換して指定。

6-7行目

サンプルコードにあったので一応入れてる。コネクションが確立してなかったら処理を終える。

9行目

spi_xfer(handle, data)でデータを送信して戻り値を得る。

  • handle
    • 4行目の戻り値。
  • data
    • 送信データ。n進数での形式混在大丈夫みたい。[]のリスト使える。

countはrx_dataの要素数。で、この時点での、戻り値格納したrx_dataのイメージが

rx_data [0] [1] [2]
バイト 1 2 3
ビット 8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1
データ 1 0 1 1 1 0 0 1 0 1 0

こんな感じ。

15行目

rx_data[0]はなんも入ってないので使わない。12bitの情報がrx_data[1]の下位4bit、rx_data[2]の8bitに分かれてるのでそれを結合する。

  • rx_data[1] << 8
    • rx_data[1]のデータを8桁左シフト。
ビット 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
rx_data[1] 1 0 1 1 0 0 0 0 0 0 0 0
  • | rx_data[2]
    • ↑のシフトしたデータと論理和を取る。
ビット 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
rx_data[1] 1 0 1 1 0 0 0 0 0 0 0 0
rx_data[2] 1 0 0 1 0 1 0
bin 1 0 1 1 0 1 0 0 1 0 1 0

そうすっとbinに結合データが入ると。やっててよかった基本情報技術者試験。2進数から10進数に変換しないと駄目かなーって思ってたら、print()したらなんか変換された。ふーん。

あとは、明るさと数値のログ取って、コード上で閾値を設定してあげりゃいいんだな。とりあえずAD変換はここまでー。次はLEDサークルライトを使えるようにしたい。

スポンサーリンク

そういえば抵抗セットを買った

eBayでresistorとかで検索すると金属皮膜抵抗でいろんな抵抗値のやつが買える。50values500pcsかつ1/2Wで900円ぐらいだった。海外からなのに送料無料だし、すげえな。ただ、ショップの説明するセット内訳が

だったんだけど、実際届いたのは

1.2 1.5 1.8 6.8 10
18 20 33 36 43
47 56 68 100 270
300 390 470 680 1k
1.2k 1.5k 2k 2.4k 2.7k
3k 3.3k 4.7k 5.6k 6.8k
8.2k 9.1k 10k 15k 18k
22k 27k 33k 39k 47k
68k 82k 100k 150k 180k
200k 220k 300k 330k 1M

であり、一致率は、まさかの44%だった。なのでそういうのが気になる人は使わないほうがいいと思う。

スポンサーリンク

参考

500 Server Error
分圧回路 - Wikipedia
光に反応する回路-光センサ回路
電子工作 初めの王道は 光モノ。 ここまでの知識で、トランジスタやオペアンプなどもからめて、光センサのCDSというモノを使って、光の状態を関知してそれに反応して動作する回路を作ってみます。
暗いときに LED を点灯させる ~ フォトレジスタとトランジスタの利用
IoT に初めて取り組む人向けの超簡単な電子工作入門サイト。電子工作の基礎から Arduino を利用したプログラミング等をわかりやすく解説
抵抗とLEDの並列接続時の電流と分流について - OKWAVE
抵抗とLEDを並列接続した場合の電流や分流について教えてください。抵抗とLEDの並列接続時には、分圧の計算式を用いて、LEDにかかる電圧を求めることができます。抵抗とLEDを並列接続する際には、抵抗B..
合成抵抗の求め方(計算方法)
合成抵抗の計算は直流回路、交流回路の計算をするときの基本になります。合成抵抗の計算は複雑そうに思えますが、基本的には、抵抗が2個の場合の直列接続または並列接続の合成抵抗の求め方さえ分かっていれば、ほとんどの回路の合成抵抗を求めることができま...
Attention Required! | Cloudflare
https://jp.rs-online.com/web/generalDisplay.html?id=ideas-and-advice/ic-package-guide

http://ww1.microchip.com/downloads/en/DeviceDoc/21298b.pdf

302 Found

https://www.cqpub.co.jp/dwm/contents/0053/dwm005300950.pdf

SPI通信 - Qiita
ネットの海を漂っていた後輩が結局よくわかっていなかったようなので説明した内容をまとめ#ちっちゃいおっさん方式での説明##頭のなかに用意するものおっさん(マスター役)その部下(スレーブ役)ホ…
「SPI」の解説
用語「SPI」の意味を解説しています。
MCP3208でA/D変換
前回のつづき。 MCP3208というICを使って、ボリュームによる電圧変化を0〜4095の4096段階に数値化している関数(readadc)の説明をする。 まずは、MCP3208でA/D変換をするにはどうすればいいのか?という基本的なことを...
pigpio ライブラリの spi_xfer() 関数を用いて Raspberry Pi 3B から SPI 接続のA/Dコンバータ MCP3208 を利用する - あらきけいすけのメモ帳
自分用の覚書。ようやく pigpio の spi_open() と spi_xfer() のパラメータの与え方が分かったので、Python と C のミニマルコードをメモしておく。 Python版 参考にしたサイト tomosoft.jp ...
pigpio library
Raspberry Pi Reg. C GPIO library and Python GPIO module and shell command utilities to control the GPIO, including SPI, ...
Raspberry Pi用pigpio Library - その7: 腰も砕けよ 膝も折れよ
pigpioライブラリの概観(その1、その2、その3、その4、その5、その6)の続き。今日は僕がpigpioを知るきっかけになったSPIについて。
5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT (8) A-Dコンバータの利用5 MCP3208 | 電子工作の環境向上
Raspberry Pi Python IoT spresense Arduino IDE LED Lチカ
Raspberry Pi用pigpio Library(番外編その2): 腰も砕けよ 膝も折れよ
先日pigpioライブラリはスレッドセーフではないということに気がついた話をした。もちろんライブラリ全体として基本的にはスレッドセーフではないことにかわりはない(全部の組み合わせでスレッドセーフ性を保証するのは大変)。しかし、今日pigpi...
ラズパイでアナログ電圧を扱う (5) MCP3208のプログラム① | 電子工作の環境向上
Raspberry Pi Python IoT spresense Arduino IDE LED Lチカ
pythonで2進数を表す - Qiita
そもそも2進数とは?2進数は英語で「binary number」と言います。いきなり2進数と言われても、意外と忘れていることも、、、(僕は完全忘れていました(笑))そもそも2進数ってなんだろ…
codeday.me
Find the best information and most relevant links on all topics related to
Python ビット演算 超入門 - Qiita
2進数に対して行うビット演算の初歩を説明します。説明にはPythonを使用します。2進数0bを付けて記述します。REPLで入力すると10進数に変換されます。>>> 0b10152進数に変換…

コメント

タイトルとURLをコピーしました