I2C接続AQMシリーズのキャラクタ表示LCDをMicro:bitで使う (3) CdS光センサ

光を感じよう

 CdS(シー・ディー・エス)は、秋月電子通商でもこんなにいろいろな種類があって、光を感じるセンサです。昔からIoTの入力デバイスに使われています。安価です。

 光を感じるセンサには、CdS以外に、

  • PINフォト・ダイオード
  • GANフォト・ダイオード
  • フォト・トランジスタ
  • フォトIC

などがあります。

 PINフォト・ダイオード、GANフォト・ダイオードなどは、波長と感度が比例します。
 CdS、フォト・トランジスタ、フォトICなどは明るさと出力が比例します。
 赤外線リモコン用は38kHzなどの受信に特化した光デバイスです。

CdSは暗いと高い抵抗値になる

 ここに正体不明のCdSがあります。測ってみれば特性はわかるので、真っ暗と、電球に近づけたときの抵抗値を測定しました。

 真っ暗;1.9MΩ、 明るい電球の直前;190Ω

 

 明るさの指標であるルクスが測れる照度計で室内の何点かを測定しました。電源Vccは3.4Vをつないでいます。

 直列抵抗R1の値を変えながら、電圧と明るさを測定しました。

1.8kΩ 8.2kΩ 82kΩ 680kΩ
電圧 [V] 明るさ [lx] 電圧 [V] 明るさ [lx] 電圧 [V] 明るさ [lx] 電圧 [V] 明るさ [lx]
0.4 67000 0.13 9600 0.11 4070 0.01 11000
0.92 1400 0.22 3200 0.43 1400 0.05 960
1.4 650 0.56 610 0.5 490 0.11 440
1.72 390 0.82 280 0.93 370 0.99 96
2.78 61 1.9 65 0.123 230    
3.1 4 3 0 0.43 92    

 目安として、廊下の明るさは20~150lx、机の上は約400lxでした。はんだ付けなどの作業はもっと明るいほうが効率的です。

条件を決める

 室内で外部の光や室内灯がついているときは無視し、近くに明るい光が付いたときには何かが起こったという設定をします。そうすると、抵抗R1は1~2kΩを使ったとき、変化がはっきりわかります。

 コラムにあるように、「何かが起こったとき」に25個あるLEDを光らそうと思ったのですが、アナログ入力がLEDの制御端子と兼用なところがあり思ったようにスケッチを描くには時間がかかりそうだったので、外部にLEDを用意しました。

 外部拡張エッジ・コネクタの中で、単独でGPIOとして利用できるのは、P12P8です。次のように、役割を決めました。

  • 平常時;P12につないだ緑色LEDを点灯
  • 何かが起こったとき;P12につないだ緑色LEDを消灯し、P8につないだ赤色LEDを点灯

 LEDのAnode側には820Ωの電流制限抵抗を直列に入れ、3.3Vの電源につなぎます。CathodeはGNDにつなぎます。


スケッチ

 CdSの出力をA0につなげて、次のスケッチを動かします。

float Vref = 3.19;
int a0;
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println("start ");
a0=analogRead(A0);
Serial.print((float)a0/1024*Vref);
Serial.println(" ");
delay(500);
}

(2020/05/09)1023は間違いなので、1024に変更した。上記のスケッチも修正したが、実行結果は変更していない

 シリアルプロッタの結果です。LED電球を0cmから約50cmまで正面に置いてからだんだんと遠ざけました。電圧は0.3Vから2.1Vまで変化しています。照度計の明るさでは12000lxから290lxの変化です。

 たとえば、10cm以内とそれ以上では明るさは極端に変化しますが、電圧は距離に比例しているように見えます。仮に0.8Vをスレッショルドに設定します。

 LEDライトをCdSに近づけると電圧は下がり、0.8V以下になると緑のLEDが点灯します。暗いと赤色のLEDが点灯します。

float Vref = 3.19;
float Volt0;

void setup() {
Serial.begin(9600);
pinMode(12,OUTPUT); //Green
pinMode(8,OUTPUT); //Red
digitalWrite(8,LOW);
digitalWrite(12,HIGH);
delay(1000);
}

void loop() {
Serial.println("start ");
Volt0=(float)analogRead(A0)/1024*Vref;
Serial.print(Volt0);
Serial.println(" ");
if ( Volt0>0.8 ) {
digitalWrite(12,LOW);
digitalWrite(8,HIGH);
} else {
digitalWrite(8,LOW);
digitalWrite(12,HIGH);
}
delay(500);
}

 上記のスケッチに、LCDへの表示を追加します。明るい状態では緑のLEDが点灯し、LCDにはbrightと表示が出ます。

#include <Wire.h>
unsigned char lcd_address = 0x3e;
float Vref = 3.19;

int i2cwritecmd(byte cmd) {
Wire.beginTransmission(lcd_address);
Wire.write((byte)0x00);
Wire.write(cmd);
return Wire.endTransmission();
}

int i2cwritedata(byte data) {
Wire.beginTransmission(lcd_address);
Wire.write(0x40);
Wire.write(data);
return Wire.endTransmission();
}

void lcdcu_set(int x, int y) { // xは桁数、0から16が指定できる。yは行数で0が1行目、1が2行目
byte ca = (x + y * 0x40) | (0x80); i2cwritecmd(ca);
}

void lcdclear() {
i2cwritecmd(0x01);delay(1);
}

void i2cprint( String pdata) {
int n = pdata.length();
for (int i = 0; i < n; i = i + 1) {
i2cwritedata(pdata.charAt(i));
delay(1);
}
}

void init_lcd() { // LCDの初期化
delay(145);
i2cwritecmd(0x38);delay(1);
i2cwritecmd(0x39);delay(1);
i2cwritecmd(0x14);delay(1);
i2cwritecmd(0x73);delay(1);
i2cwritecmd(0x56);delay(1); // 3.3V
i2cwritecmd(0x6c);delay(300);
i2cwritecmd(0x38);delay(1);
i2cwritecmd(0x0c);delay(2);
i2cwritecmd(0x01);delay(2);
}

float Volt0;

void setup() {
Wire.begin();
init_lcd();
i2cprint(String("Start"));
Serial.begin(9600);
pinMode(12,OUTPUT); //Green
pinMode(8,OUTPUT); //Red
digitalWrite(8,LOW);
digitalWrite(12,HIGH);
delay(1000);
}
void loop() {
lcdclear();
Serial.println("start ");
Volt0=(float)analogRead(A0)/1024*Vref;
lcdcu_set(0,0);i2cprint("A0 CdS=");
i2cprint(String(Volt0)); i2cprint("V");
Serial.print(Volt0);
Serial.println(" ");
if ( Volt0>0.8 ) {
digitalWrite(12,LOW);
digitalWrite(8,HIGH);
lcdcu_set(0,1);
i2cprint("dark");
} else {
digitalWrite(8,LOW);
digitalWrite(12,HIGH);
lcdcu_set(0,1);
i2cprint("bright");
}
delay(500);
}

コラム LEDはマトリクス配置

 Micro:bitには5×5のLEDが実装されています。本文のようにアナログ入力の結果をもとにGo/noGOとかの表示に使えるのではないかと丸や四角を表示を試みました。

 一つのLEDを光らせるには、

  • ROW(行)をLOW('0')
  • COL(列)をHIGH('1')

にします。ROWとCOLがそれぞれ5ずつならわかりやすいのですが、ROWは三つ、COLは九つと変形マトリクスです。

 例えば、左上のLEDだと次のように記述します。外部拡張エッジ・コネクタにはGPIOとしてP0~P20までの名称が振られています。入力もしくは出力に使えるので、出力(OUTPUT)として使うことを記述してから、'1'もしくは'0'を出力します。

pinMode(3, OUTPUT);
digitalWrite(3, LOW);
pinMode(26, OUTPUT);
digitalWrite(26, HIGH);

 25個すべてのROWとCOLのピン番号一覧です。複雑なマトリクスです。

3,26 23,27 4,26 24,27 10,26
23,28 24,28 25,28 9,28 7,28
4,27 6,26 10,27 6,28 3,27
7,26 9,26 25,26 24,26 23,26
10,28 9,27 3,28 25,27 4,28

 ROWの3本のピン番号は、外部拡張エッジ・コネクタに出ていません。

  ピン番号
ROW 1 26
2 27
3 28

 COLの9本のうち6本は外部拡張エッジ・コネクタに出ています。そのうち3本がアナログ入力と兼用です。

COL ピン番号 アナログ入力
1 3 A3
2 4 A4
3 10 A5
4 23  
5 24  
6 25  
7 9  
8 7  
9 6  

 兼用なので、アナログ入力にある程度の電圧が入力されていてROWをLOWにすると、LEDが光ります。5×5のマトリクスなので、ROWが5本あるべきなところ、3本しかありません。そのため、思ってみなかったLEDが光ります。

 何種類かのマーク表示を作りました。スケッチloop()の中で、いずれかのコメントを外して実行します。

◆shikaku

◆maru

◆nihon

◆igeta

void setup() { 
// ROW
pinMode(26, OUTPUT);
pinMode(27, OUTPUT);
pinMode(28, OUTPUT);
}

void shikaku() {
pinMode(24, OUTPUT);
pinMode(25, OUTPUT);
pinMode(9, OUTPUT);
digitalWrite(24, 0);
digitalWrite(25, 0);
digitalWrite(9, 0);
digitalWrite(28, 1);
delay(1);
digitalWrite(28, 0);
pinMode(6, OUTPUT);
digitalWrite(6, 0);
digitalWrite(26, 1);
delay(1);
digitalWrite(26, 0);
pinMode(24, OUTPUT);
pinMode(25, OUTPUT);
pinMode(9, OUTPUT);
digitalWrite(24, 0);
digitalWrite(25, 0);
digitalWrite(9, 0);
digitalWrite(26, 1);
delay(1);
digitalWrite(26, 0);
}

void nihon() {
pinMode(24, OUTPUT);
pinMode(9, OUTPUT);
digitalWrite(24, 0);
digitalWrite(9, 0);
digitalWrite(28, 1);
delay(1);
digitalWrite(28, 0);
pinMode(6, OUTPUT);
digitalWrite(6, 0);
digitalWrite(26, 1);
digitalWrite(28, 1);
delay(1);
digitalWrite(26, 0);
digitalWrite(28, 0);
pinMode(24, OUTPUT);
pinMode(9, OUTPUT);
digitalWrite(24, 0);
digitalWrite(9, 0);
digitalWrite(26, 1);
delay(1);
digitalWrite(26, 0);
}

void igeta() {
pinMode(23, OUTPUT);
pinMode(24, OUTPUT);
digitalWrite(23, 0);
digitalWrite(24, 0);
digitalWrite(27, 1);
delay(1);
digitalWrite(27, 0);
pinMode(23, OUTPUT);
pinMode(7, OUTPUT);
digitalWrite(23, 0);
digitalWrite(7, 0);
digitalWrite(28, 1);
delay(1);
digitalWrite(28, 0);
pinMode(23, OUTPUT);
pinMode(7, OUTPUT);
digitalWrite(23, 0);
digitalWrite(7, 0);
digitalWrite(26, 1);
delay(1);
digitalWrite(26, 0);
pinMode(6, OUTPUT);
digitalWrite(6, 0);
digitalWrite(26, 1);
digitalWrite(28, 1);
delay(1);
digitalWrite(26, 0);
digitalWrite(28, 0);
pinMode(9, OUTPUT);
pinMode(25, OUTPUT);
digitalWrite(9, 0);
digitalWrite(25, 0);
digitalWrite(27, 1);
delay(1);
digitalWrite(27, 0);
}

void maru() {
pinMode(23, OUTPUT);
pinMode(24, OUTPUT);
digitalWrite(23, 0);
digitalWrite(24, 0);
digitalWrite(27, 1);
delay(1);
digitalWrite(27, 0);
pinMode(23, OUTPUT);
pinMode(24, OUTPUT);
pinMode(25, OUTPUT);
pinMode(9, OUTPUT);
pinMode(7, OUTPUT);
digitalWrite(23, 0);
digitalWrite(24, 1);
digitalWrite(25, 1);
digitalWrite(9, 1);
digitalWrite(7, 0);
digitalWrite(28, 1);
delay(1);
digitalWrite(28, 0);
pinMode(7, OUTPUT);
pinMode(9, OUTPUT);
pinMode(25, OUTPUT);
pinMode(24, OUTPUT);
pinMode(23, OUTPUT);
digitalWrite(7, 0);
digitalWrite(9, 1);
digitalWrite(25, 1);
digitalWrite(24, 1);
digitalWrite(23, 0);
digitalWrite(26, 1);
delay(1);
digitalWrite(26, 0);
pinMode(9, OUTPUT);
pinMode(25, OUTPUT);
digitalWrite(9, 0);
digitalWrite(25, 0);
digitalWrite(27, 1);
delay(1);
digitalWrite(27, 0);
}

void loop(){
//shikaku();
//nihon();
igeta();
//maru();
}