Python × Pico 電子工作 | #11 明るさを測ろう

今回は CdS(カドミウム硫化物セル、フォトレジスタとも) を使って、周囲の明るさを数値で読み取る 実験をします。CdS は 光が当たると抵抗値が小さくなる 不思議な部品で、オレンジ色の小さな円盤の表面にギザギザの蛇行模様が描かれているのが特徴です。街灯の自動点灯、トンネル進入時のヘッドライト ON、デジカメの露出計など、「明るさで動くもの」のセンサーとして昔から使われている定番部品 です。

  • CdS(フォトレジスタ) の動作原理(光導電効果)
  • 抵抗値が変化するセンサーを 分圧回路(10kΩ と直列) で ADC 入力に変換する方法
  • 明るい時 / 暗い時の ADC 値の違いと、しきい値判定 の使い方
  • #02 / #05 / #08 で学んだ LED・PWM・LCD と組み合わせた応用
  • CdS の RoHS 規制問題と 代替部品(フォトトランジスタ) について

今回も PycoBlocks(MicroPythonモード) の 「アナログ入力」 ブロック(#05 のセンサー回で使った可変抵抗ノブと同じブロック)を使います。違うのは 外付け回路 だけ。可変抵抗ノブを 「CdS + 10kΩ 抵抗の分圧回路」 に置き換えるだけで、周囲の明るさが ADC 値(0〜65535)として読めるようになります。

明るさを測ろう

CdS の仕組み:光が当たると抵抗が下がる素子

CdS は 硫化カドミウム(CdS)の薄膜 に光が当たると 電子が励起されて電流が流れやすくなる(光導電効果)という性質を利用した素子です。オレンジ色の円盤に くねくねとした蛇行模様の電極 が見えますが、あの蛇行は 光を受ける面積を大きくしながら電極間の距離を短く保つ ための工夫で、感度を上げると同時に応答を速くしています。

環境CdS の抵抗値(目安)分圧 ADC 値(目安)
真っ暗(ふた・指で覆う)1MΩ 以上500 以下(HIGH 寄り)
薄暗い室内(夕方)50kΩ 前後10000〜20000
普通の室内(蛍光灯下)5kΩ 前後40000〜50000
明るい窓辺・直射日光1kΩ 以下60000〜65535

抵抗値が変わるセンサー(CdS のほか、サーミスタ・湿度センサーなど)は、そのままでは ADC に繋げません(ADC は電圧の高さを読む装置だから)。そこで 固定抵抗(10kΩ)と直列につないで分圧回路 を組み、抵抗の変化を電圧の変化に変換 してから ADC に入れます。

分圧回路の仕組み:抵抗の変化を電圧の変化に変える

分圧回路 とは、2 本の抵抗を直列につないで、2 本の境目から中間の電圧を取り出す 仕掛けです。電圧の式は Vout = Vin × R2 ÷ (R1 + R2)。今回は R1 = CdS(可変抵抗)、R2 = 10kΩ(固定) として、中間点の電圧を Pico の ADC ピン(GP26)で読みます。

CdS と 10kΩ の分圧回路と、明るい時/暗い時の ADC 値の変化を示した概念図
分圧回路の概念図:3V3 → CdS → GP26 → 10kΩ → GND。明るいと CdS の抵抗が下がって中間電圧が上がり ADC 値が大きく、暗いと逆に ADC 値が小さくなる。
# 分圧回路の配線図
#
#   3V3 ─[ CdS ]─┬─→ Pico GP26(ADC0)
#                │
#             [ 10kΩ ]
#                │
#               GND
#
# 明るい:  CdS = 1kΩ  →  Vout = 3.3 × 10k/(1k+10k) ≈ 3.0V(高い → ADC 大)
# 暗  い:  CdS = 1MΩ  →  Vout = 3.3 × 10k/(1M+10k) ≈ 0.03V(低い → ADC 小)

💡 覚え方:明るさと ADC 値の向き

  • 明るい → CdS の抵抗が小さい → 上の抵抗が小さい → Vout が高い → ADC 値 大
  • 暗い → CdS の抵抗が大きい → 上の抵抗が大きい → Vout が低い → ADC 値 小
もし CdS と 10kΩ を逆順(上:10kΩ / 下:CdS)に組むと、明るい時に ADC 値が小、暗い時に ADC 値が大、という 逆の挙動 になります。本記事では「直感に合う」前者(CdS が上)の配線で進めます。
CdS + 10kΩ 分圧回路を Pico に接続した実体配線図
配線:CdS を 3V3 と GP26 の間に、10kΩ を GP26 と GND の間に挟む分圧回路。CdS は明るいほど抵抗が下がるので、GP26 の電圧は明るいほど高くなります。(実体配線図は Fritzing の部品画像をもとに作図・CC BY-SA 3.0)

ステップ1:明るさを 1 回だけ測ってみよう

まずは 1 回だけ明るさを測定 し、結果を print で表示します。「アナログ入力」 ブロックの ADC ピンを GP26 にして、変数 light に値を入れます。「値を表示」 をフォーマット文字列と組み合わせて、Light: 32768 のように分かりやすい形で出力しましょう。

CdS で 1 回明るさを測定して print するブロック
ブロック構成:アナログ入力 GP26 → light → 値を表示「Light: {light}」。
from machine import ADC
_adc26 = ADC(26)
light = _adc26.read_u16()
print(f'Light: {light}')

動作確認:CdS の表面を 指で覆ったり、スマホのライトを当てたりしてから実行ボタンを押すと、出力される数値が大きく変わります。

  • 指で覆う(暗い) → 500〜5000 程度
  • 蛍光灯下(普通) → 30000〜50000 程度
  • スマホライトで照射 → 60000〜65535 程度
数値は 環境光や CdS の個体差 で前後するので、「自分の環境で何の値か」 を実測して覚えるのが大切です。

ステップ2:0.5 秒ごとに連続して明るさを表示しよう

1 回測るだけでは 明るさの変化 を追えません。「ずっと繰り返す」 ブロックの中に明るさ測定を入れて、0.5 秒ごとに ADC 値を表示 し続けるようにしましょう。Thonny のシェルに数字が並んで、手をかざしたり離したりすると数値が変化するのが見えます。

0.5秒ごとに明るさを測定して表示するループブロック
ブロック構成:ずっと繰り返す → アナログ入力 GP26 → light → 値を表示 → 0.5 秒待つ。
from machine import ADC
import utime
_adc26 = ADC(26)
while True:
    light = _adc26.read_u16()
    print(f'Light: {light}')
    utime.sleep(0.5)

「しきい値」を決める実験:演習で 「暗くなったら点灯」 を作るために、まずは 自分の環境で「暗いと感じる時の値」 を計測しておきましょう。室内の照明を消した状態・スタンドだけ点けた状態など、境目になる場面の数値をメモ しておくと演習でしきい値が決めやすくなります。本記事では 20000 を「暗い/明るい」の境界として採用します(蛍光灯下の薄暗い室内に相当)。

📝 応答速度の注意

CdS は 応答時間が 10〜100 ms 程度 あり、瞬間的に光ったストロボには反応が遅れます。速い変化を捉えたい場合は フォトトランジスタ(数μs で応答) の方が向いていますが、室内の明暗判定のような遅い変化 なら CdS で十分です。

演習課題

課題3-11-1:分圧回路の ADC 値を計算しよう

今回の配線「CdS(上)+ 10kΩ(下)+ Vin = 3.3V」で、CdS の抵抗値が以下のとき、中間点の電圧と ADC 値 はそれぞれいくつになるか計算してみよう。

分圧の公式: Vout = Vin × R2 ÷ (R1 + R2)
ADC 換算: ADC = Vout × 65535 ÷ 3.3

問1: CdS = 1kΩ(直射日光)のとき Vout と ADC は?
問2: CdS = 10kΩ(明るい室内)のとき Vout と ADC は?
問3: CdS = 100kΩ(薄暗い夕方)のとき Vout と ADC は?
問4: CdS と 10kΩ の上下を入れ替えると挙動はどう変わる?

▶ 模範解答と解説を見る
# 問1: CdS=1k, R=10k, Vin=3.3
Vout = 3.3 * 10000 / (1000 + 10000)
# Vout ≈ 3.00V  →  ADC ≈ 3.00 × 65535 / 3.3 ≈ 59577(明るい)

# 問2: CdS=10k, R=10k
Vout = 3.3 * 10000 / (10000 + 10000)
# Vout = 1.65V  →  ADC ≈ 32768(ほぼ中央)

# 問3: CdS=100k, R=10k
Vout = 3.3 * 10000 / (100000 + 10000)
# Vout = 0.30V  →  ADC ≈ 5957(薄暗い)

# 問4: CdS と 10kΩ を入れ替えると分子・分母が変わる
# 入れ替え後: Vout = 3.3 × R_CdS / (10k + R_CdS)
# 明るい時: CdS=1k → Vout=3.3×1k/11k ≈ 0.30V(ADC 小)
# 暗い時:  CdS=1M → Vout=3.3×1M/(10k+1M) ≈ 3.27V(ADC 大)
# → 明暗で ADC 値の大小が逆転する。本記事の if-else の条件式も逆向きに書き直す必要あり

解説: 解説:分圧回路の 2 本の抵抗の比 が中間電圧を決めます。「CdS と直列に組む抵抗値」 はおおむね 普通の室内での CdS 抵抗値(5〜10kΩ)に合わせると、明暗で ADC 値が 中央付近を境にスイッチする ように動きます。もし暗い時の値ばかりが出る(ADC が常に小さい)なら、固定抵抗を 1kΩ に下げる、明るい時の値ばかりが出る(ADC が常に大きい)なら、固定抵抗を 100kΩ に上げるという調整で感度のスイートスポットを探せます。抵抗のカラーコード:

  • 10kΩ:茶・黒・橙・金(4 色帯)
  • 1kΩ:茶・黒・赤・金
  • 100kΩ:茶・黒・黄・金

課題3-11-2:暗くなったら自動で点灯する常夜灯を作ろう

CdS の 定番応用 です。「変数 light が 20000 未満なら内蔵 LED(GP25)を点灯」、「20000 以上なら消灯」 という条件分岐を作ります。「ずっと繰り返す」 の中に 「もし light < 20000 だったら / そうでなければ」 を入れて、0.2 秒間隔で判定します。完成したら部屋の電気を消して、ベッドサイドの常夜灯のように動かしてみましょう。

▶ 模範解答と解説を見る

ブロックの組み合わせ例:

暗くなったら内蔵LEDを点灯するブロック
ブロック構成:ずっと繰り返す → アナログ入力 GP26 → light → もし light < 20000 なら LED(GP25)点灯、そうでなければ消灯 → 0.2秒待つ。
from machine import ADC, Pin
import utime
_adc26 = ADC(26)
while True:
    light = _adc26.read_u16()
    if light < 20000:
        Pin(25, Pin.OUT).value(1)
    else:
        Pin(25, Pin.OUT).value(0)
    utime.sleep(0.2)

解説: 動作確認のコツ:CdS を指で覆ったり、部屋の照明を消したりすると、Pico の 内蔵 LED(基板上の緑色 LED) が自動で点灯します。明るいときに反応しすぎる 場合は 20000 を 10000 に下げ、真っ暗にしないと点かない 場合は 30000 に上げて調整しましょう。発展:

  • 外付け LED でナイトライトに進化:内蔵 LED の代わりに #02 で学んだ 外付け LED(GP15 + 220Ω + LED + GND)を点灯させると、本物の常夜灯として使える
  • ヒステリシス(チラつき防止):「light が 15000 未満で点灯、25000 以上で消灯」のように 2 つのしきい値 を使うと、境界付近のノイズで LED がチラチラするのを防げる
  • 明るさに応じた PWM 制御:pwm.duty_u16(65535 - light) のように 暗いほど明るく光るLED 制御も実装可能(cs_b で生 Python を書く)

課題3-11-3:LCD に明るさを「%」と「数値」で表示する明るさメーターを作ろう

CdS と #08 で学んだ I2C LCD を組み合わせて、卓上明るさメーター を作ります。LCD の 1 行目に ADC 生値、2 行目に「%」変換した値 を表示。0.5 秒ごとに更新し続ければ、写真撮影の露出計のように使えます。

配線追加:CdS + 10kΩ 分圧(GP26)に加えて、I2C LCD を Pico の GP0(SDA)・GP1(SCL)に接続します。LCD の VCC は 5V(VBUS)、GND は GND に。CdS の GP26(ADC)と LCD の GP0/GP1(I2C0)はぶつからないので安心です。

▶ 模範解答と解説を見る

ブロックの組み合わせ例:

CdSの明るさをLCDに%表示するブロック
ブロック構成:LCD初期化 SDA:GP0 SCL:GP1 → ずっと繰り返す → アナログ入力 GP26 → light → LCD 1行目「Light: {light}」→ LCD 2行目「Pct: {light*100/65535} %」→ 0.5秒待つ。
from machine import ADC, Pin, I2C
from pico_i2c_lcd import I2cLcd
import utime
_i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
lcd = I2cLcd(_i2c, 0x27, 2, 16)
_adc26 = ADC(26)
while True:
    light = _adc26.read_u16()
    lcd.move_to(0, 0); lcd.putstr(f'Light: {light}')
    lcd.move_to(0, 1); lcd.putstr(f'Pct: {light * 100 / 65535} %')
    utime.sleep(0.5)

解説: 動作確認のコツ:LCD 2 行目の 「%」表示 は light * 100 / 65535 の計算結果がそのまま出るので、43.21786... のように 小数点以下が多い ことがあります。見た目を整えたい場合は int(light * 100 / 65535) で整数に丸めるか、f-string の書式指定 {light * 100 / 65535:.1f} で小数第 1 位までに揃えると見やすくなります(現状の PycoBlocks ブロックでは cs_b で生 Python を書く必要あり)。発展:

  • 明るさをグラフバーで表示:LCD の 2 行目を '#' * int(light * 16 / 65535)のように 16 文字分の棒グラフ にすると、ビジュアルで明るさが分かるレベルメーター風に
  • 「明」「暗」「中」の文字で表示:if-elif-else で「light > 40000 → 明」「light > 15000 → 中」「それ以下 → 暗」の 3 段階を 2 行目に表示
  • データロガー化:1 分ごとに ADC 値を記録して、時刻ごとの明るさ推移を Thonny のシェルに CSV 風に print → コピーして Excel で折れ線グラフ化

CdS + I2C LCD を Pico に接続した実体配線図
課題3-11-3 の配線:CdS + 10kΩ 分圧(3V3→CdS→GP26→10kΩ→GND)に加えて、I2C LCD(VCC→VBUS 5V、SDA→GP0、SCL→GP1、GND→GND)を接続。ブレッドボードの電源レール(赤=5V、青=GND)を上手に使うと配線がすっきりまとまります。

まとめ

  • CdS(フォトレジスタ) は光が当たると抵抗が下がるセンサー。オレンジ色の蛇行模様の円盤が特徴。
  • 抵抗値の変化は 10kΩ と直列の分圧回路 で電圧の変化に変換してから ADC で読む。
  • 「アナログ入力」ブロックは #05 で使ったのと同じ。違うのは外付け回路だけ。
  • 明るい → ADC 値 大、暗い → ADC 値 小(CdS が上の配線の場合)。しきい値(例:20000) で if-else 判定すれば自動点灯ライトが作れる。
  • #02 LED・#05 PWM・#08 LCD と組み合わせて、常夜灯・明るさメーター・データロガー など多彩な応用が可能。
  • 応答時間 10〜100ms とやや遅いので、速い変化を捉えたい場合はフォトトランジスタ(数μs 応答)を使う。

📝 CdS の RoHS 規制について

CdS には カドミウム(Cd) という有害物質が含まれており、EU の RoHS 指令 で原則として商用製品への使用が禁止されています。教育用キットや日本の学校現場では今でも使われていますが、本格的な製品開発の現場では フォトトランジスタ(NPN フォト IC) やBH1750 などのデジタル照度センサー に置き換わっています。学習・実験には CdS が分かりやすくて便利、と覚えておくと良いでしょう。

次の記事は #12「サーボモーターを動かそう」。PWM で角度を指定して動くサーボモーター(SG90)を扱い、ロボットアームや自動開閉ドアの基礎技術を学びます。

トップへ戻る