編集履歴一覧に戻る
syouwa-taroのアイコン画像

syouwa-taro が 2021年02月18日21時08分47秒 に編集

初版

タイトルの変更

+

”テレビから離れて!” 子供に注意して聞かないとテレビを消すBOX

タグの変更

+

Arduino

+

TV

+

thermopilearray

+

Ecoモード

+

防犯モード

+

childlock

メイン画像の変更

メイン画像が設定されました

本文の変更

+

# 初めに 子供はテレビにかじりつくように見る事が多い。ママは目が悪くなることを心配して”テレビから離れて”といつも注意。忙しいママに代わって注意してくれるBOX。注意しても聞かないとテレビOFFしてしまう。(離れればまたON)。その他⓵在室の時だけテレビONにするEco機能 ➁暗闇でも体温で侵入者を検知する防犯機能 がある。一台3役の優れもの。 # 仕組み ① 子供の接近を検知する方法として、超音波距離計があるがテレビの近くに物があったり部屋で反響したりして誤動作が多い。シャープ赤外距離計は1mくらいまでが限度で視野角も狭い。サーモグラフが安定していた。ただし距離を測るデバイスでなく体温の占める面積でおよその距離とした。(面積多い=近距離) テレビの前にBOXを置き子供が傍に来ると(視野角60度 2~3m)警告音を鳴らし同時に親の声を録音していた音声ボードで”テレビから離れて”を再生。更に1mくらいまで接近すると、長い警告音のあとリモコンで電源OFFする。その後テレビを離れるとリモコンで電源ONする。 ② Ecoモードでは、テレビの部屋に人がいる(60度~4m)ことを検知するとテレビONし退出すると一定時間後テレビOFFする。従来のエコテレビに比べ、体温検知により安定したON OFF検出が行える。 ③ 防犯モードでは、暗闇でジッとしている不審者でも体温で検出(60度 ~7m)し、一般の焦電型の防犯センサでは動きのない物には反応しないため防犯効果は大きい。 ##### その他 テレビのリモコンは電源コードがON・OFFトグル動作で、BOXはテレビの電源状態がわからないので電源コードを送るとON OFFを逆転させてしまう恐れがある。これを防ぐ為リモコンコードで強制ONコード 強制OFFコードによりテレビの電源のコントロールを行う。リモコンコードは公開されていないので自力で調べるしかない(>_<)。今回ソニーのテレビでリモコンコードを調べ強制ON 強制OFFにより ON OFFの間違えを防いでいる。 # 主な部品 マイコン         arduino NANO サーモグラフ       AMG8833基板(IIC pullupがされている) 音声ボード        ISD1820 (録音/再生機能 アンプがついている) 小型SP 圧電Buzzer    (*圧電SPではない) 赤外線LED 2個 3positionスライドSW   1回路3接点  電源PushSW  006Pスナップ ケース          100均 ![BOX の中](https://camo.elchika.com/0e3d8b648f22b12d5d0612dd14b6764a910f5552/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376461636366622d643365352d343236642d386436312d3538383537333535363238662f35313563316265322d313634632d343436622d383736622d356234616530323262376639/) ![テレビから離れて回路図](https://camo.elchika.com/9c3bca26423f04b8ad8e34fb68d7cd1cf1562ba5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376461636366622d643365352d343236642d386436312d3538383537333535363238662f39303639393962312d633636312d346538382d383764322d353839646163396565656633/) @[youtube](https://www.youtube.com/watch?v=72v7YlVMhac&feature=emb_logo) # 結果 5才くらいまでの孫で”実験”したところ、意外にも皆きちんとBOXの言うとうりテレビに近ずくときちんと戻りましたヽ(^o^)丿 小学生低学年でも”くだらない”とも言わず音声をたのしんでいました。予想外だったのはBOXの音声ボードはマイクが付いていて録音も出来るので自分の声を録音して弟たちに注意していました。みんなで楽しめた工作でした。またサーモグラフは値段も下がりホビーでも使えるようになり、今後いろいろ応用が楽しみです。 # 参考文献 [Conta サーモグラフィー AMG8833搭載](https://www.switch-science.com/catalog/3395/) [赤外線アレーセンサAMG8833をWebSocketで見てみる](https://neocat.hatenablog.com/entry/2018/01/29/023945) [M5stickCで赤外線リモコン・・・](https://siroitori.hatenablog.com/entry/2020/04/25/114250) [M5stickCでちゃんと動く赤外線リモコンを作る](https://note.com/ssktkr/n/ne9f289509bea) # ソースコード ```arduino:テレビから離れて #include <Wire.h> #define PCTL 0x00 #define RST 0x01 #define FPSC 0x02 #define INTC 0x03 #define STAT 0x04 #define SCLR 0x05 #define AVE 0x07 #define INTHL 0x08 #define TTHL 0x0E #define INT0 0x10 #define T01L 0x80 #define AMG88_ADDR 0x69 // in 7bit gyroMPU6050 IIC68h //temporaryData to buffer sumtotal IIC ADlow68h ADhigh69h int TMPDAT0[64], TMPDAT1[64], TMPDAT2[64], TMPDAT3[64], TMPDATS[64]; int TMPHISairdata=0; //**object scan ** int INITSTcnt = 0; //初期設定から打ち4LOOP int TMPmax = 0, TMPmin = 400, TMPair = 0; int lmax = 0, lmin = 0, imax = 0, imin = 0; int TMPHIS[350], TMPHISmax; //int TMPHIS[150];TMPHISmax; //TMPHIS[150]NG int MANflg, WASHflg; int TMPHISmanS=0; int TMPmaxmin=500; //minをセット「するためinitは高め:300~350 //int cdsON,cdsOFF; //**system** int POWflg = 1; //flg=0 だとSWONで一旦PIPIと鳴る //**IR Remote** #include <IRremote.h> IRsend irsend; void setup() { Serial.begin(115200); Wire.begin(); //A4:SDA A5:SCL pinMode(3, OUTPUT); pinMode(2,OUTPUT);digitalWrite(2,LOW); //赤外LED 2:GND 3:OUT pinMode(12,OUTPUT);pinMode(13,OUTPUT);pinMode(11,OUTPUT);digitalWrite(11,LOW);pinMode(10,OUTPUT); //12,13:BUZZER 11:FT MIC 10:音声Trigger pinMode(4,OUTPUT);digitalWrite(4,HIGH); //D4:Keyscan 出力:keyscanの時だけLOWにしてD5 D6 D7 のpullup lossを防ぐ pinMode(5,INPUT_PULLUP);pinMode(6,INPUT_PULLUP);pinMode(7,INPUT_PULLUP); //Keyscan入力 D5:childLock D6;alarm D7:eco enegy savimg int fpsc = B00000000;// 1fps datasend(AMG88_ADDR, FPSC, &fpsc, 1); int intc = 0x00; // diff interrpt mode, INT output reactive datasend(AMG88_ADDR, INTC, &intc, 1); // moving average output mode active int tmp = 0x50; datasend(AMG88_ADDR, 0x1F, &tmp, 1); tmp = 0x45; datasend(AMG88_ADDR, 0x1F, &tmp, 1); tmp = 0x57; datasend(AMG88_ADDR, 0x1F, &tmp, 1); tmp = 0x20; datasend(AMG88_ADDR, AVE, &tmp, 1); tmp = 0x00; datasend(AMG88_ADDR, 0x1F, &tmp, 1); int sensorTemp[2]; dataread(AMG88_ADDR, TTHL, sensorTemp, 2); // Serial.print("sensor temperature:"); // Serial.println( (sensorTemp[1]*256 + sensorTemp[0])*0.0625); } //setup END void loop() { INITSTcnt++; if (INITSTcnt >=50) INITSTcnt = 50; //初期設定から打ち5回以上 TMPmax = 0; TMPmin = 400; TMPHISmax = 0; //max min RESET Serial.println("["); // Wire library cannnot contain more than 32 bytes in bufffer // 2byte per one data // 2 byte * 16 data * 4 times = 2byte*64point int sensorData[32]; //TMPHISCLR(); //histgram clear initで設定する場合 for (int i = 0; i < 4; i++) { // read each 32 bytes dataread(AMG88_ADDR, T01L + i * 0x20, sensorData, 32); for (int l = 0 ; l < 16 ; l++) { int16_t temporaryData = (sensorData[l * 2 + 1] * 256 ) + sensorData[l * 2]; //TMPHIS[temporaryData] += 1; //温度ヒストグラム+1 初期設定する場合 //*** TMPDATSUM();*** 温度Data sum3回で安定化 TMPDATS[i * 16 + l] = 0; //TMPDATS[i*16+1]=0; TMPDAT0[i * 16 + l] = temporaryData; TMPDAT1[i * 16 + l] = TMPDAT0[i * 16 + l]; //TMPDAT1[i*16+l]=TMPDAT0[i*16+l]; TMPDATS[i * 16 + l] = TMPDATS[i * 16 + l] + TMPDAT1[i * 16 + l]; //TMPDATS[i*16+l]=TMPDATS[i*16+1]+TMPDAT1[i*16+1]; TMPDAT2[i * 16 + l] = TMPDAT1[i * 16 + l]; //TMPDAT2[i*16+1]=TMPDAT1[i*16+l]; TMPDATS[i * 16 + l] = TMPDATS[i * 16 + l] + TMPDAT2[i * 16 + l]; //TMPDATS[i*16+l]=TMPDATS[i*16+1]+TMPDAT2[i*16+1]; TMPDAT3[i * 16 + l] = TMPDAT2[i * 16 + l]; //TMPDAT3[i*16+1]=TMPDAT2[i*16+l]; TMPDATS[i * 16 + l] = TMPDATS[i * 16 + l] + TMPDAT3[i * 16 + l]; //TMPDATS[i*16+l]=TMPDATS[i*16+1]+TMPDAT3[i*16+1]; //*TMPDATSUMmaxmin* if (TMPmax < TMPDATS[i * 16 + l]) { TMPmax = TMPDATS[i * 16 + l]; imax = i; lmax = l; } //if (TMPmax<TMPDATS[i*16+1]) {TMPmax=TMPDATS[i*6+1];imax=i;lmax=l;} if (TMPmin > TMPDATS[i * 16 + l]) { TMPmin = TMPDATS[i * 16 + l]; //(TMPDATS[i * 16 + l]); //Serial.print(temporaryData);//Serial.print(temperature); imin = i; lmin = l; } Serial.print(TMPDATS[i * 16 + l]); //Serial.print(temporaryData);//Serial.print(temperature); if ( (l + i * 16) < 63 ) Serial.print(","); if (l == 7) Serial.println(); } //for:I16 END Serial.println(); //every 16data } //for:i4 END Serial.println("]"); //every 64data //****************************************1frame Data complete 3回から打ちしてからmain loop******** if (INITSTcnt >=5 ) //3回から打ちしてからMain loop { airtemperature(); //air temperature histgraghmaxで決定 objectscan() ; //object Scanで探す } //********************************************************************** Serial.print(TMPmax);Serial.print(" maxads"); Serial.print(imax * 16 + lmax + 1);Serial.print(" maxmin"); Serial.println(TMPmaxmin); Serial.println(TMPmin); Serial.print(TMPair);Serial.print(" aircells");Serial.println(TMPHISmax);//Serial.println(INITSTcnt); Serial.print(POWflg);Serial.print(" mancells"); Serial.println(TMPHISmanS);// // Serial.print(cdsON);Serial.println(cdsOFF); delay(1000); } //loop END //*********loop END************* void datasend(int id, int reg, int *data, int datasize) { Wire.beginTransmission(id); Wire.write(reg); for (int i = 0; i < datasize; i++) { Wire.write(data[i]); } Wire.endTransmission(); } void dataread(int id, int reg, int *data, int datasize) { Wire.beginTransmission(id); Wire.write(reg); Wire.endTransmission(false); Wire.requestFrom(id, datasize, false); for (int i = 0; i < datasize; i++) { data[i] = Wire.read(); } Wire.endTransmission(true); } void airtemperature() //温度別ヒストグラム:最大sample数=Air温度 { TMPHISmax=0; TMPHISCLR(); //時間をかけてrefresh 安定基準 for (int air=63;air>=0;air--) { TMPHISairdata=TMPDATS[air]; //TMP[350]にすると相対温度修正不要 TMPDATS[air]-250;NG TMPHIS[TMPHISairdata]++; //0=<TMP<350 TEMP<400 配列確保NG 150<TEMP<400 0<配列<250 if (TMPHIS[TMPHISairdata]>TMPHISmax) {TMPHISmax=TMPHIS[TMPHISairdata]; TMPair=TMPHISairdata; //=TMPHISairdata+150;//   } } //**人の面積 TMPmax値のmin値(人のいないところのmax値)を探し それ以上の個数をHISTGRMで合計=人の面積 if (TMPmaxmin>TMPmax) TMPmaxmin=TMPmax; TMPHISmanS=0; //init reset //maxmin+8以上の温度の個数を合計 air温度以上のhistgramの個数を合計:manが近ずくと増加 for ( int TMPHISman=349;TMPHISman > TMPmaxmin + 8;TMPHISman--) //温度:349からmaxmin+8まで:人温度スレシホールド設定まで個数を合計 TMPmaxminは温度データー配列温度計算はー150 +(スレシホールド) { TMPHISmanS=TMPHISmanS+TMPHIS[TMPHISman]; } //Serial.println(TMPHISmanS); } // for (int tmp = 149; tmp > 50; tmp--) // { if (TMPHIS[tmp] > TMPHISmax) { // TMPHISmax = TMPHIS[tmp]; // TMPair = tmp; // } // } //} void TMPHISCLR() { for ( int m = 349; m >= 0; m--) { //TMPHIS[350]で相対温度補正なし  [150]NG [250] 150<TEMP<150+250=400 TMPHIS[m] = 0; } } //END void objectscan() { digitalWrite(4,LOW); //digitalWrite(4,LOW); //SW checkの時だけkeyscan 4pin出力Lowにして D5 D6 D7 Pullupのlossを防ぐ int sensorValchlk = digitalRead(5); //0:child lock int sensorValalm = digitalRead(6); //0:alarm int sensorValeco = digitalRead(7); //0:eco enegy saving digitalWrite(4,HIGH); if (sensorValeco == 0) eco(); //eco? else if (sensorValchlk == 0) childlock(); //child lock? else if (sensorValalm == 1) alarm(); //alarm? } //END void childlock() //IR POWERON { //TVOFFのとき2コマ以下ON   3コマ以上PiなしのSPEECH****************************** if (POWflg<=0) { // if (POWflg==0) {  //off していて人がちいさくなったらon case POWlg if (TMPHISmanS <= 2) { //2コマ以下だとON領域判断 chatter防止2~9 no action TMPmaxmin-140(温度で+10:2=4m) //cdsOFF = analogRead(A0); //ONする前のcdsValue for (int i = 0; i < 3; i++) { irsend.sendSony(0xa90, 12); //0xa90,12= 22 POWON/OFF delay(40); } POWflg=1; // POWflg=1;  pi(); //ピッ ON sound      //delay(1000); //cdsON = analogRead(A0); //ONした後のcdsValue } //2コマ以下ON判断END else { speech(); //TVOFF & 3コマ以上なので speechで警告  PIはなし TVONで接近と区別 } } //POWflg OFF END else //TVONで11コマ以上=OFF  3~10コマ警告領域 2コマ以下TVONのまま *************************************** { if (POWflg>=1) { //onしていて人がきたらoff case POWflg if (TMPHISmanS >= 11) { //11コマ以上あるとOFF領域に入った判断 /chatter防止2~9 no action  TMPmaxmin-140(温度で+10:10=1m) //cdsON = analogRead(A0); //OFFする前の cdsValue for (int i = 0; i < 3; i++) { irsend.sendSony(0xa90, 12); //0xa90,12= 22 POWON/OFF delay(40);// delay(40);  } POWflg=0; pii(); speech(); //ピー― OFF sound //cdsOFF = analogRead(A0); //OFFした後のcdsValue //if (cdsOFF<cdsON) irsend.sendSony(0xa90,12); //ON OFF 逆だったので再度 } else //**************************ALARM************************************** { if (TMPHISmanS>=3 ) { pi(); speech(); delay(500);} //pi(); //3コマ~10コマまでは警告領域判断 }//Object scan END } //offcase POWflg END } // POWflg else END } //END void pf() { } void speech() { digitalWrite(10,HIGH); delay(500); digitalWrite(10,LOW); } void pii() { digitalWrite(11,HIGH); //FT:H for (int s=600;s>0;s--) { digitalWrite(13,HIGH); digitalWrite(12,LOW); delayMicroseconds(400); digitalWrite(13,LOW); digitalWrite(12,HIGH); delayMicroseconds(400); } digitalWrite(11,LOW); //FT:L } void pi() { digitalWrite(11,HIGH); //FT:H for (int s=80;s>0;s--) { digitalWrite(13,HIGH); digitalWrite(12,LOW); delayMicroseconds(200); digitalWrite(13,LOW); digitalWrite(12,HIGH); delayMicroseconds(200); } digitalWrite(11,LOW); //FT:L } void pipi() { digitalWrite(11,HIGH); //FT:H pi(); delay(100); pi(); digitalWrite(11,LOW); //FT:L } void iron() { for (int i = 0; i < 3; i++) { irsend.sendSony(0xa90, 12); //0xa90,12= 22 POWON/OFF delay(40); } } void iroff() { for (int i = 0; i < 3; i++) { irsend.sendSony(0xa90, 12); //0xa90,12= 22 POWON/OFF delay(40); } } void eco() //void eco() { if ((POWflg<=0)&&(TMPHISmanS>=2)) { //POWOFFで人がいるのでIRON pi(); iron() ; POWflg=4; //POWflg=10;  //人がいなくなってOFFするまでのWAIT時間設定 4秒 } //if END if ((POWflg==1)&&(TMPHISmanS<2)) { //POWONで人がいないのでIROFF pipi(); iroff(); // POWflg=0; } //if END if ((POWflg>1)&&(TMPHISmanS<2)) { //人がいなくなってOFFするまでWait時間 POWflg--; //IROFFまでのWAIT時間 } //if END } //END void alarm() { if (TMPHISmanS>=2) {pi(); delay(100); pi(); delay(100); pi();delay(100); pi(); delay(1000);} //>=1 誤動作 } ```