ソリッドステートリレーをマイコンで制御してシーリングライトをリモコン操作したい。ESP32やArduinoを使った赤外線リモコン受信機は作例がいくつもあるが、PICマイコンを使ったものは作例が少ないようだ。PIC12F1822 で組んでみる。

ソリッドステートリレーはフォトカップラーでトライアックを駆動する基板を使う。TTL/CMOSの出力でAC100Vの入り切りができる。電源は5Vが要るのでAC100Vから作った。PICは低消費電力なのでこれでも大丈夫だろう。

IRreceiver

FullSizeRender FullSizeRender
FullSizeRender



PICのプログラム
Arduinoの場合は赤外線リモコン通信のライブラリがあり、それを使えば簡単であるが、PICでは受信信号を解析して判読するプログラムを書く必要がある。

使用したリモコン送信機の信号フォーマットはNEC(4バイト)である。
これをデコードするプログラムだが、受信されてきた順番で信号のビットを左詰めにして8ビット毎に1バイトに数値化する処理を4回行う。下は、送信コード21hをデコードすると84hになる例。
 IMG_1147
受信信号のビット判定(0か1か)にはOFF時間の長さを測定する必要がある。やり方として、タイマーで経過時間を測定したり、タイマー割り込みを使う方法があるようだが、delayを使う方法が最もシンプルだ。受信信号がOFFになったら0.1msecのdelayをループさせてループ回数をカウントする。OFF状態が完了したらループを終了させ、「ループ回数×0.1」 が OFF時間[msec]となる。


// ****************************************************************************** // IR remote control receiver (NEC 4byte format) // This program is developed from reference project posted in the site linked bellow. // http://zattouka.net/GarageHouse/micon/InfraredCOM/InfraredCOM.htm // // 2022.5.29 #pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled) #pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input) #pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled) #pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled) #pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) #pragma config IESO = ON // Internal/External Switchover (Internal/External Switchover mode is enabled) #pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled) // CONFIG2 #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) #pragma config PLLEN = OFF //PLL #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled) #include <xc.h> #include <stdio.h> #define _XTAL_FREQ 16000000 // delay用に必要(クロック16MHzを指定) #define IrIN RA5 // IR受信モジュール信号入力 #define SW PORTAbits.RA2 //リレー駆動出力 #define CHECK PORTAbits.RA4 #define HIGH 1 #define LOW 0 unsigned int IrCmdRcv(unsigned char MyDeviceNo); unsigned int DataCheck(unsigned char MyDeviceNo, char *dt); void main(){ unsigned int revdData; unsigned int sel_count = 0; OSCCON = 0b01111010 ; // 内部クロックは16MHzとする ANSELA = 0b00000000 ; // すべてデジタルI/Oに割当 TRISA = 0b00100000 ; // RA5だけ入力その他のピンは出力に割当てる(RA3は入力専用) PORTA = 0b00000000 ; // 出力ピンの初期化(全てLOWにする) WPUA = 0b111111; // 入力は全て弱プルアップ if( eeprom_read(0) == 1){ SW = 0; //前回起動中にONした記録がある。その場合、最初はOFFとする。(復電時にONしないようにする) } else{ SW = 1; //前回起動時にONしていなかった。ONにする。(電源の再投入起動でONにする) } eeprom_write(0, 0); //ON情報をクリアしておく。 // Pin 状態変化 初期化 ------------------------------------- // SW = 1; IOCANbits.IOCAN5 = 1; // RA5負極性の変位でWake-up INTCONbits.IOCIE = 1; //IO変化による割り込みを許可 while(1){ // 繰り返しループ IOCAFbits.IOCAF5 = 0; // 入力変位フラグをクリア SLEEP(); // 無信号時はスリープ // ****************************** revdData = IrCmdRcv(0x84); //SanoR'e Remote Control のデバイスコード(実際の送信バイトデータのビット並びと逆であることに注意) switch(revdData){ case 0x20DF: // 全光 スイッチON SW ^= 1; eeprom_write(0, 1); //リモコンでONしたことを記憶しておく break; case 0: // 指定デバイスコードではなかった場合、またはチェックエラーの場合 CHECK=1; __delay_ms(100); CHECK=0; break; default: break; } } } unsigned int DataCheck(unsigned char MyDeviceNo, char *dt) { unsigned char x1 , x2 , i; // デバイスコード1バイト目の読み込みとチェック x1 = 0 ; for (i=0 ; i<8 ; i++) { if (*dt++ == 1) x1 = x1 | (1 << i) ; } if ((x1 != 255) && (x1 != MyDeviceNo)) return(0) ;// 受信デバイスコードが自分のコードではない場合はゼロを返す。 // デバイスコード2バイト目の読み込み x2 = 0 ; for (i=0 ; i<8 ; i++) { if (*dt++ == 1) x2 = x2 | (1 << i) ; } // データ1バイト目の読み込み x1 = 0 ; for (i=0 ; i<8 ; i++) { if (*dt++ == 1) x1 = x1 | (1 << i) ; } // データ2目の読み込みとチェック(データ1の反転データとなっているはず) x2 = 0 ; for (i=0 ; i<8 ; i++) { if (*dt++ == 1) x2 = x2 | (1 << i) ; } if ((x1 ^ x2) != 0xFF) return(0) ; // 反転チェックNGの場合はゼロを返す。 return((unsigned int)(x1 << 8 | x2)); // チェック正常、キーデータを返す } unsigned int IrCmdRcv(unsigned char MyDeviceNo){ char IRbit[34]; unsigned long t; unsigned int ans, i; ans = 0; t = 0; // リーダ部チェック if(IrIN == LOW) { t = 0; while(IrIN == LOW){ __delay_us(100); // OFF(データ1)の時間を測る。  t++; } } //リーダーなら処理する if(t>= 50){ // 5msec以上あったら i = 0; while(IrIN == HIGH) ; //リーダのHIGH部分が終わるのを待つ //データ部読み込み開始 while(i < 32){ // 16bit x 2 のデータ長を解析するので32ビット分を繰り返す。 while(IrIN == LOW) ; t = 0; while(IrIN == HIGH){ __delay_us(100); // ON(データ0)の時間を測る t++; } if(t >= 10) IRbit[i] = 1; //10msecより長ければ'1' else IRbit[i] = 0; //10msecより長ければ'0' i++; } ans = DataCheck(MyDeviceNo, IRbit); } return(ans); }


赤外線リモコンでオンオフできるようになったので、ESPマイコンの赤外線送信機を使ってAlexaで音声操作が出来るようになった。

FullSizeRender


■参考にした情報