赤外線LEDによる照明の自動化(その2)・・・ 照明自動化システムを作る

今回は、照明自動化システムのハードウェアの製作について紹介します。

照明自動化システム概要

室内照明自動化システムは、人感センサー、照度センサーからの情報を基に、マイコンが条件判断し照明器具を自動でON/OFFします。

人感センサー

人感センサーは、人が発する赤外線を検出するセンサーで、自動ドアの開閉や自動照明などに良く使われています。今回使ったのは、下記の焦電人感センサです。

焦電型赤外線センサーモジュール(焦電人感センサ) SB612A

製品名:焦電型赤外線センサーモジュール(焦電人感センサ) SB612A

この製品は、感度、検知出力保持時間、動作照度の調整ができるタイプで、秋月電子で600円で購入しましたが、照度センサーが後付けとなっている安価なタイプも使えます。

このタイプはアマゾンで2個145円で販売されており、2回目以降はこの安価なタイプを購入しました。(現在は1個163円)

両タイプはセンサー出力に違いがあり、通常タイプがオープンコレクタ出力に対し、安価なタイプは、電圧出力となっています。

照度センサー

次に照度センサーですが、今回下記のフォトトランジスタを使用しました。

照度センサ(フォトトランジスタ) 560nm NJL7502L

製品名:照度センサ(フォトトランジスタ) 560nm NJL7502L

この製品の特長は、感度特性が人間の視感度に近いこと(波長560nm)と、環境上問題のあるCdSセルの代替品として採用されていることです。秋月電子で2個で100円で購入しました。

赤外線発光LED (指令用)

赤外線を発信するLEDは、下記を使いました。

製品名:5mm赤外線LED OSI5FU5111C-40

この製品は、秋月電子で5個で100円で購入しました。

LEDに流す電流は大きいので、マイコンからの直接駆動は出来ず、トランジスタ駆動とします。

データシートより、If=100mA(パルス1000mA)、Vf=1.35Vなので、LEDに200mAを流す場合、制限抵抗は、R = (3.3-1.35)/0.2 = 9.75Ω。したがって、10Ωとしました。

照明自動化システム回路図

マイコンはWiFi機能が付いたESP-Wroom-02を使用しています。(サーバーへデータを送信するため)

照明自動化システム回路図
照明自動化システム回路図

回路図では、人感センサーは安価なタイプの回路を示しており、センサー出力をトランジスタで受けコレクタ出力をマイコンへ入力しています。

照度センサーの入力は、AD変換ピン(Tout)へ接続していますが、ESP-Wroom-02のAD入力は1VMAXのため分圧する必要があります。

回路図では100kと40kの抵抗で分圧し3.3Vを2.36Vと0.943Vに分圧しています。左側にある可変抵抗は感度調整用です。

照明自動化システムプログラム

プログラムリストを下記に示します。

このプログラムには、温度・湿度のモニタリングも含まれ、照度データは温度・湿度データとともに1分毎にサーバーへ保存され、ヒストリカルトレンドとして、WEB画面で見ることが出来ます。

<pre class="C++">
/* ロケーション 102 書斎
* 温度入力、赤外線リモコン信号送信、焦電型人感センサー入力、照度センサー入力、プログラム
*/

extern "C" {
#include "user_interface.h"
}
#include
#include
#include "DHT.h"
#define PROG_NAME "(TempHumidIR_2)"
#define DHTTYPE DHT11 // DHT 11
#define DHT_PIN 12
#define LED_BLINK 13
#define IFROutputPin 16 // 赤外線リモコンLED出力
#define PIRInputPin 4 // 人感センサー入力
#define LeaderCodeDuration_ON 2660
#define LeaderCodeDuration_OFF 2660
#define CarrierCodeDuration_ON 880
#define CarrierCodeDuration_OFF 1840
#define TurnOnIlluminance 30.0
#define DelayTimer 300 // basically 15 sec
Ticker ticker1;
DHT dht(DHT_PIN, DHTTYPE);
const char* ssid = "my_SSID";
const char* password = "my_PassWord";
const char* host = "192.168.0.100";
static int count_sec = 5; // 60 sec counter
static int sub_counter=10;
static int dht_sec = 10; // 60 sec counter for DHT sensor
static int pir_on = 0;
static int delay_timer_ON = 0;
static int delay_timer = -1;
static int every_sec = 5;
static int light_sta = 0;
static float illuminance;
static float illuminance_prev;
static int count_send;
static char str_time[10]; // 00:00:00
void DebugPrint(char *pnt, String str){

if( false // プリントをする場合、コメントアウト
// || ( pnt== "light")
// || ( pnt== "Timer")
// || ( pnt== "adc" )
// || ( pnt== "sendIR" )
// || ( pnt== "WiFi" )
|| (pnt== "Debug" )
)
Serial.println("(" + String(&str_time[0]) + ") " + str);
}
void count_down() { // 100ミリ秒ごとのタイマー割込み
sub_counter--;
if(sub_counter <= 0){
sub_counter = 10;
count_sec--;
dht_sec--;
every_sec--;
if(delay_timer > 0){
delay_timer--;
}else if(delay_timer == 0){
delay_timer_ON = 1;
}
}
switch(light_sta){
case 0: // 消灯状態
if(count_sec %2 == 0){digitalWrite(LED_BLINK, LOW);}else{digitalWrite(LED_BLINK, HIGH);}
break;
case 1: // 点灯状態へ移行
if( (count_sec %2 == 0) || (sub_counter == 9) || (sub_counter == 7) || (sub_counter == 5) ){digitalWrite(LED_BLINK, LOW);}else{digitalWrite(LED_BLINK, HIGH);}
break;
case 2: // 点灯状態
if( (sub_counter >= 6) ){digitalWrite(LED_BLINK, LOW);}else{digitalWrite(LED_BLINK, HIGH);}
break;
case 3: // サーチモード
case 4: // サーチモード
if( (sub_counter == 10) || (sub_counter == 8) || (sub_counter == 6) || (sub_counter == 4) || (sub_counter == 2) ){digitalWrite(LED_BLINK, LOW);}else{digitalWrite(LED_BLINK, HIGH);}
break;
case 5: // 消灯状態へ移行
if( (sub_counter == 10) || (sub_counter == 8) || (sub_counter == 6) ){digitalWrite(LED_BLINK, LOW);}else{digitalWrite(LED_BLINK, HIGH);}
break;
default:
break;
}
}
void pir_intrpt(){
if(light_sta == 0){
DebugPrint("Debug", "[INTRPT] pir=" + String( digitalRead(PIRInputPin)) + " pir_on=" + String(pir_on) +" illuminance=" + String( get_illuminance()));
}
pir_on = 1;
// digitalWrite(LED_PIRInput, digitalRead(PIRInputPin) );
}
int get_illuminance(){
return ( system_adc_read()*0.163 ); // =*1000/40*2.39/1024*3.3
}
void setup() {
Serial.begin(115200);
delay(10);
ticker1.attach_ms(100, count_down);
pinMode(LED_BLINK, OUTPUT);
// pinMode(LED_PIRInput, OUTPUT);
pinMode(IFROutputPin, OUTPUT);
Serial.println("DHT11 init");
dht.begin();
// We start by connecting to a WiFi network
Serial.println();
Serial.println( PROG_NAME);
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);

// WiFi.PrintDiag(Serial);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
pinMode(PIRInputPin, INPUT_PULLUP); // change from INPUT to INPUT_PULLUP
attachInterrupt(digitalPinToInterrupt(PIRInputPin), pir_intrpt, FALLING ); // 割り込みを設定する
// ticker2.attach_ms(10, roter_count); // check input pulse level every 10msec
// Move to Initial Position
light_sta = 0;
// search_count = 0;
}
void send_data(int data) {
if(data == 0){ // ... output 0
ifr_carrier(CarrierCodeDuration_ON * 0.82);
digitalWrite(IFROutputPin, LOW);
delayMicroseconds(CarrierCodeDuration_ON);

}else if(data == 1){ // ... output 1
ifr_carrier(CarrierCodeDuration_ON * 0.82);
digitalWrite(IFROutputPin, LOW);
delayMicroseconds(CarrierCodeDuration_OFF);

}else if(data == 2){ // ... Leader Code
ifr_carrier(LeaderCodeDuration_ON * 0.82);
digitalWrite(IFROutputPin, LOW);
delayMicroseconds(LeaderCodeDuration_OFF);

}else if(data == 3){ // ... Stop Code
ifr_carrier(CarrierCodeDuration_ON * 0.82);
digitalWrite(IFROutputPin, LOW);
delayMicroseconds(LeaderCodeDuration_OFF);
}
}
void ifr_carrier(int period){
/* carrier frequency 8.7micro sec on 17.5 micro sec off */
// int On=8;
// int Off=17;
int num_carrier = (int)(period/(26.3));
for(int i=0; i>= 1;
}while(++j<8);
}
send_data(3);
}
void loop() {
int ADC_Value = 0;
static int OhmDataON[] = { 0x78 };
static int OhmDataOFF[] = { 0xb4 };
static char url[50];
static char str[150];
static char line[1000];
char *p;
int pir = digitalRead(PIRInputPin);
switch (light_sta) {
case 0: // 消灯状態
if( pir_on == 1 ){ // 人感センサー割込みフラグ発生時に行う処理⇒点灯する //pir_on == 1 ||なし
illuminance = get_illuminance();
Serial.println("Case 0: [PIR]:" + String(pir) + "[PIR_ON]:" + String(pir_on));
// Serial.println("Turning on light(pir) [PIR]:" + String(pir) + "[PIR_ON]:" + String(pir_on));
pir_on = 0;
if( ( illuminance < TurnOnIlluminance )){ // Human Body Detected (LOW) & darkness -> 点灯指令
// 上記にあった(pir == 0) の条件を外す。つまり人感センサーがOFFとなってしまっても、pir_on で点灯する。
// illuminance_prev = get_illuminance();
DebugPrint("Debug", "STA=0( illuminance < TurnOnIlluminance); pir=" + String(pir) + " pir_on=" + String(pir_on) +" illuminance=" + String(illuminance));
send_code(OhmDataON);
light_sta = 2; // 点灯指令
every_sec =1;
count_send=1;
delay_timer = DelayTimer; // timer

}else if(( illuminance >= TurnOnIlluminance )){ // (pir == 0) を外す。
DebugPrint("Debug", "STA=0( illuminance > TurnOnIlluminance); pir=" + String(pir) + " pir_on=" + String(pir_on) +" illuminance=" + String(illuminance));
light_sta = 2; // 点灯状態とみなす
delay_timer = DelayTimer; // timer
}
}
break;
case 1: // 点灯指令、点灯待ち状態 Human Body detected & light_off & dark -> 点灯。
if( every_sec <= 0 ){
light_sta = 2;
/* illuminance = get_illuminance();
// if( illuminance > illuminance_prev * 1.2 ){ // 1.2倍以上の照度で、点灯を確認する
// 点灯状態
// myservo_1.attach(ServoOut1);
// search_count=1;
delay_timer = DelayTimer; // timer
// }else{
// illuminance_prev = get_illuminance();
if(count_send<2){
send_code(OhmDataON); // 繰り返し点灯指示を送る
// }else{
light_sta = 0; // 消灯状態とみなす。
// search_count=1;
// pir_on = 0;
// }
Serial.println("Case 1: Turning on light: " + String(++count_send));
}*/
}
break;
case 2: // 点灯状態
// pir = digitalRead(PIRInputPin);
if(delay_timer_ON ==1){
delay_timer--;
delay_timer_ON = 0;
if( (pir == 1)){ // Nobody detectic(High) -> Nobody & timeup -> 消灯指令。
illuminance_prev = get_illuminance();
send_code(OhmDataOFF);
Serial.println("Turn off light(delay_timer_UP): " + String( sizeof(OhmDataOFF)/sizeof(int) ));
light_sta = 3; // 消灯指令へ
pir_on = 0;
count_send=1;
}else{
delay_timer = DelayTimer;
}
}else if( every_sec <= 0 ){
if(pir == 0){
delay_timer = DelayTimer; // timerをリセット
} // timerをリセット
}
break;
case 3: // 消灯指令、消灯待ち状態 // 10/14:右端で停滞していたので、修正。
if( every_sec <= 0 ){
if(count_send>2){
light_sta = 0;
pir_on = 0;
}else if(pir == 1){
++count_send;
Serial.println("Turning off light: " + String(count_send));
}else{
light_sta = 0;
pir_on = 1;
}
}
break;
// pir = digitalRead(PIRInputPin);
Serial.println("Case 3: [PIR]:" + String(pir) + "[PIR_ON]:" + String(pir_on));
illuminance = get_illuminance();
if( (illuminance < illuminance_prev * 0.8) || (illuminance < TurnOnIlluminance)){ // 消灯確認 0.8以下か、または TurnOnIlluminance以下

// Serial.println("Case 3: [count_send]:" + String(count_send) + "[PIR]:" + String(pir) + "[PIR_ON]:" + String(pir_on));
if(count_send>2){
light_sta = 0;
/*if(pir == 1){
light_sta = 0; // 消灯状態
pir_on = 0; // 消灯の時にセンサー信号がONとなるので、この部分は必要。

}else{
light_sta = 0; // 消灯状態
pir_on = 1; // 消灯の時にセンサー信号がONとなるので、この部分は必要。
}*/

}else{
// illuminance_prev = get_illuminance();
if(count_send<2){ // 5から2に下げる(失敗は今のところ見られない)
// send_code(OhmDataOFF); // 消灯を確認するまで繰り返し行う
}else if( (pir == 1) ){ // 2回以上繰り返している場合は、 ⇒消灯とみなす(一旦点灯させるのを止める)
// illuminance_prev = get_illuminance();
// Serial.println("Turning off light(pir=0): ");
light_sta = 0; // 点灯指令⇒消灯
// search_count=1;
pir_on = 0;
//send_code(panaDataON);
//count_send=1;
}else{
light_sta = 0; // 消灯状態
pir_on = 1; // 消灯の時にセンサー信号がONとなるので、この部分は必要。
}
Serial.println("Turning off light: " + String(count_send));
}
++count_send;
}
break;
default:
break;
}
//*** for Searching Code ****
if( every_sec <= 0 ){
// pir = digitalRead(PIRInputPin);
// Serial.println("[DeayTimer]:" + String(delay_timer)+" [light_sta]:"+String( light_sta));
// Serial.println("light_status: " + String(light_sta) +", illuminance_prev: " + String(illuminance_prev));
// Serial.print("[ILLUMINANCE]ADC:" + String(ADC_Value)+ ", "+String(ADC_Value*1000/1024)+"mV "+String(get_illuminance()/3.3)+"uA ");
// Serial.println( String(get_illuminance())+"Lux"); // 白色LEDは3.3倍
DebugPrint("Timer", "[DeayTimer]:" + String(delay_timer) + " [light_sta]:" + String( light_sta) +"[PIR]:" + String(pir) + "[PIR_ON]:" + String(pir_on));
String message = "light_status: " + String(light_sta) +", illuminance_prev: " + String(illuminance_prev);
DebugPrint("light", message);
//AD変換と照度の表示。
ADC_Value = system_adc_read();
DebugPrint("adc", "[ILLUMINANCE]ADC:" + String(ADC_Value)+ ", "+String(ADC_Value*1000/1024)+"mV "+String(get_illuminance()/3.3)+"uA "+ String(get_illuminance())+"Lux");
//データステータス
/*
memset(str, 0, 5);
static int code = 0;
// Serial.println("Comm Buffer :" + String(Serial.available()));
if(Serial.available() >= 2){
for(int i=0; i<2; i++){
str[i] = Serial.read();
}
while(Serial.available()){Serial.read();}
Serial.flush();
*/
}
//*************/
if( every_sec <= 0 ){ every_sec = 1; }
/*** 通常処理 ***/
if( count_sec <= 0 ) { // 数秒周期
count_sec = 2;
}else if ( dht_sec <= 0){
dht_sec = 60;
// DHT-11による温度、湿度検出、データ送信
float h = dht.readHumidity();
float t = dht.readTemperature();
// int ADC_Value = 0;
// ADC_Value = system_adc_read();

// Serial.print("connecting to ");
// Serial.println(host);
DebugPrint("WiFi", "connecting to " + String(host));
// Use WiFiClient class to create TCP connections
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("TM connection failed");
return;
}

// We now create a URI for the request
String url = "/roomenvr/rcv_data2.php";
url += "?HD102=";
url += h;
url += "&TD102=";
url += t;
url += "&IN102=";
url += get_illuminance();
// Serial.print("Requesting URL: ");
// Serial.println(url);
DebugPrint("WiFi", "Requesting URL: " + String(url));

// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop();
break;
}
}
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
String line = client.readStringUntil('\r');
;
p = strstr(&line[0], ":Data Inserted");
// Serial.print(line);
DebugPrint("WiFi", String(line));
if(p){
memset(str_time, 0, 10);
strncpy(&str_time[0], p-8, 8);
dht_sec = 63 - atoi(&str_time[6]);
// Serial.println("[time]: " + String(&str_time[0]) + " " + String(&str_time[6]) + "second" );
// Serial.println( dht_sec);
}
}
if(dht_sec <= 0) dht_sec=60;
// Serial.print("Info:GetCounter=");
// Serial.println();
// Serial.print("closing connection");
// Serial.println( PROG_NAME);
DebugPrint("WiFi", "closing connection");
}

}
</pre>

照明モニタリング画面

サーバーから送られる照明モニタリング画面を下記に示します。

サーバーのデータベースに保存された照度データがブラウザに表示されている。

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