Vol.887 26.Jan.2024

ペンシル型の電動ドライバー 赤外線リモコン(10)自動解読LCD表示

D ペンシル型の電動ドライバー

by fjk

 小さなネジを使った通販の組立キットシリーズの分解・修理を考えているが、このネジを回すのに小型の電動ドライバーがないか探していたところ、ペンシル型の電動ドライバーをAmazonで見つけ、入手した。
 ドライバーとビットがセットになっているものは、ケースも付属しているので収納に便利だが、使えそうなビット(4mm)を持っていたのと、BOXビット(6角)も使いたかったので、本体(AMIR、電動精密ドライバー、2,479円)のみとビット(MOVMAO 130精密ドライバーセット、1,999円)を別々に購入した。
 ペンシル型の電動ドライバーをネットで探すと、多数の候補が見つかるが、電動だけでなく、「手動でも使える」と記述があることが決め手となった。送られてきた物は、透明な丸いプラスチックケースに収まったドライバー本体とUSBケーブル、英語で書かれた簡単な説明書のみだった。。


電動ドライバーと透明ケース

USB-C充電式

「手動でも使える」とある

 購入したビットセットは、BOXビットだけのものでも良かったのだが、ビットを磁化して使いたいので、マグネタイザーもセットになっているものを探していると、ビットだけでなく手動ドライバーなども一緒の安価なセットを見つけ、これをゲット。セットには4mmビットを6mmのドライバーにも使えるように「電動ドリル用ホルダー」や「延長シャフト」、「フレキシブルシャフト」、「オープナー」、「SIMカード取り出しピン」なども付属し、メガネはもちろん、スマホの分解・組立にも使えるようだ。


手動ドライバーと付属ツール

ケースの反対側に103種のビット

 早速メガネのネジやナットの増し締めや、コンベックスの修理に使ってみたが、堅いネジを手動で緩め、その後、そのまま続けて電動で廻せるのは便利である。ネジを締める場合も、最後に手動で増し締めができるのは安心である(他社の物も手動で使えるかもしれないが、手動を保証しているのは安心)。
 ドライバーのトルクはそんなに大きくないが、元々小さなネジを対象にしているので、ネジを壊さないのでちょうど良い。軽く握っていると、本体が滑って回り出すところがちょうど良い按配かな。


R 赤外線リモコン (10) 〜IRコード自動解読・LCD表示

by fjk

 前レポート(abc886)で、SONY/NECリモコンの赤外線コードを自動で解読することが出来たが、やはりコードの表示も欲しくなった。そこで、AQM1602Y(秋月電子、P-11916)を使ったLCD表示モジュールをミニブレッドボードで作ることにした。折角なので、(今回は使用していないが)LCD表示器の他に2個のプッシュスイッチも取り付けた。
 なお、この汎用LCD表示モジュールは他のボード開発時にも利用出来る(秋月マニュアルでAQM1602Yの電源電圧が3.3Vとなっているが、ST7032のVddは2.7〜5.5V、ロジックは最大6.0Vまで。5V使用の場合、VoltageBooster回路を使用しないのでコンデンサを接続していない)。


LCD表示モジュールの配線

LCD表示器を取り付けた状態

 LCD表示ライブラリ(i2cLCD_ST7032i.c(zip)i2cLCD_ST7032i.h(zip))を使用するため、PIC16F18424(ROM:4k、RAM:512)と機能・ピン配置が同じでメモリが多いPIC16F18426(ROM:16k、RAM:2k)を使用した。赤外線の受信・解読にはabc886の回路をそのまま使用しても良いが、狭いところに無理にはめ込んだ部品もあるので、部品配置を少し変更した。また、スイッチとLEDのポートも入れ替え(PC2<−>PC4)、これに伴いピン設定とプログラムも修正した。
 なお、LCD表示ライブラリの詳細はabc856を、MPLAB-Xでライブラリの使い方はabc840を参照。



PIC16F18426を使ったIR解読回路

 MCC設定は、MSSP1の追加とピン設定を変更した以外はabc886のままとした。また、LCD表示プログラムはabc856をほぼ利用し、SONY/NECコードモードと検出ビット数を1行目に表示し、2行目にはスクロール機能を使用しなかったので、16進コードが16文字に収まるよう表示を少し変更した。
 なお、SMTwin入力に外部ピン(RA2)を使わないようにCLC2出力を設定してみたが、RA2を汎用ピンとして使用できなかった。


MCC設定画面(PinModule)

MSSP1設定

 受信したSONY/NEC赤外線リモコンコードを自動判定し解読・表示することが出来るようになったが、SONYコード解析時にMARKが1500uSを超える場合があり、パルス幅判定値を1600uSに変更した。
 なお、コードの表示は送信バイト順。SONYコードはリピートをそのまま表示。


SONYコード例
(黄LED点灯)

NEC(IOdata)コード例
(黄LED消灯)

【プログラム】 abc887s.c(zip)(修正済み)
 ※ 解析プログラムにバグ(SONYコードの最後のバイトが表示されない)があり、修正しました。
 
/****************************************************************
 * SMT Hight-Low mode で、赤外線リモコンのパルス幅をus単位で
 * 測定し、EUSARTからUSB変換器経由でPCに送信する
 * 同時にLCD(AQM1602Y)にも表示する
 ****************************************************************/

#include "mcc_generated_files/mcc.h"
#include "i2cLCD_ST7032i.h"

#define maxdata 75                                      // 赤外線データー配列数

uint16_t PW[maxdata];                                   // Mark配列
int16_t  PR[maxdata];                                   // Space配列
uint8_t  p_PW;                                          // Mark配列ポインタ
uint8_t  p_PR;                                          // Space配列ポインタ

uint8_t  Sony;                                          // Sonyフォーマット?
char     sBf[20];                                       // 文字列作業エリア

void prt_Hex(uint8_t w, uint8_t n){                     // 16進数表示
    if(n){
        sprintf(sBf,"%02X ",w);
        printf("  >> %02XH\n\r",w);
    }else{
        sprintf(sBf,"%02X",w);
        printf("  >> %02XH",w);  
    }
    LCD_str(sBf);                                       // LCD表示
 }

/*====    Main application    ==== */
void main(void)
{
    uint8_t ird;                                        // 赤外線データ
    uint8_t sbt;                                        // ビットセット用シフトバイト
    uint8_t bct;                                        // 有効ビットデータカウンタ
    uint8_t sgf;

    SYSTEM_Initialize();                                // デバイス初期化
    LCD_init();

    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();
    LCD_str("ready");  
    
    while (1)                                           // 赤外線信号受信 繰り返し
    {
        p_PW = 0;                                       // データポインタ初期化
        p_PR = 0;
        SMT1PWAIF = 0;                                  // フラグクリア
        SMT1PRAIF = 0;
        SMT1IF = 0;
        SMT1CON1bits.SMT1GO = 1;                        // 測定開始
        printf("\r\nready\r\n");                        // 準備完了メッセージ送信
        
        // ---------- 赤外線信号を受信する --------
        while((p_PR < maxdata)){                        // バッファ満杯まで繰返
            if(SMT1PWAIF){                              // mark終了なら
                SMT1PWAIF = 0;                          // フラグクリア
                PW[p_PW++]=SMT1CPW;                     // High時間を保存
            }
            if(SMT1PRAIF){                              // space終了なら
                SMT1PRAIF = 0;                          // フラグクリア
                if(SMT1CPR < 10000){                    // 10ms未満なら
                    PR[p_PR++]=SMT1CPR;                 //   そのままLow時間を保存
                }else{                                  // 10ms以上なら
                    PR[p_PR++]=-(SMT1CPR / 1000);       //   msに変換し負数で保存
                }
            }
            if(SMT1IF){                                 // 無信号が100ms続いたら
                SMT1IF = 0;                             // フラグクリア
                PR[p_PR++]=0;                           // 無信号記号「0」を保存
                break;
            }
        }
        SMT1CON1bits.SMT1GO = 0;                        // 測定終了

        //---------- 受信結果を解析・送信する -------
        ird = 0;
        sbt = 1;
        bct = 0;
        sgf = 0;

        LCD_clr();
        if(PW[0]<3000){
            Sony = 7;                                   // -> SONY Mode
            IO_RC2_SetHigh();
            sprintf(sBf,">SONY, %d",p_PW);
            LCD_str(sBf);
            puts(sBf);
        }else{
            Sony = 0;                                   // -> NEC Mode
            IO_RC2_SetLow();
            sprintf(sBf,">NEC, %d",p_PW);
            LCD_str(sBf);
            puts(sBf);
        }

        printf(" -  mark  space (us)\r\n");             // タイトル送信
        LCD_cursor(0,1);
        for(char i = 0;i < p_PW;i++){                   // 保存した時間を送信
            printf("%2d %5d",i,(uint16_t)PW[i]);        // mark時間
            if(PR[i]==0) {                              // 無信号なら
                printf("    ---");                      //  「---」を送信
                if(Sony){
                    if(PW[i] < 1600){                   // markが1600us未満なら
                        bct++;
                        if(PW[i] > 900){                // markが900us超なら
                            ird |= sbt;                 // シフトバイトを加える
                            printf("  1");
                        }else{
                            printf("  0");
                        }
                        prt_Hex(ird, 0);                // 16進数で表示
                    }
                }
            }else if(PR[i] > 0){                        // 10ms未満なら
                printf("  %5d",PR[i]);                  //   us単位で送信(改行を外す)
                if (Sony){                              // *== SONY Code (Mark) ==
                    if(PW[i] < 1600){                   // markが1600us未満なら
                        sgf = 1;                        // 有効データ
                        if(PW[i] > 900){                // markが900us超なら
                            ird |= sbt;                 // シフトバイトを加える
                            printf("  1");
                        }else{
                            printf("  0");
                        }
                    }
                }else{                                  // *== NEC Code (Space) ==
                    if(PW[i] < 1500){                   // markが1500未満なら
                        if(PR[i]<2000){                 // spaceが2000未満なら
                            sgf = 1;                    // 有効データ
                            if(PR[i]>900){              // さらにspaceが900以上なら
                                ird |= sbt;             // シフトバイトを加える
                                printf("  1");
                            }else{
                                printf("  0");
                            }
                        }
                    }
                }
                if(sgf){                                // 有効データなら 
                    sbt <<= 1;                          // シフトバイトを左へシフト
                    bct++;                              // 有効データ数を1増やす
                    if(Sony){                           // コードがSONYなら
                        if(bct == Sony){
                            prt_Hex(ird, 0);            // 16進数で送信(表示)
                            ird = 0;                    // 赤外データをクリア 
                            sbt = 1;                    // シフトバイトも初期化  
                            Sony = bct + 8;
                        }
                    }else{                              // コードがSONY以外(NEC)
                        if((bct % 8) == 0){             // データ数が8の倍数なら
                            prt_Hex(ird, 0);            // 16進数で送信(表示)
                            ird = 0;                    // 赤外データをクリア 
                            sbt = 1;                    // シフトバイトも初期化  
                        }
                    }
                    sgf = 0;
                }
            }else{                                      // 10ms超なら
                printf("%7d (ms)",-PR[i]);              //   ms単位で送信
                 if(Sony){
                    if(PW[i] < 1600){                   // markが1600us未満なら
                        bct++;
                        if(PW[i] > 900){                // markが900us超なら
                            ird |= sbt;                 // シフトバイトを加える
                            printf("  1");
                        }else{
                            printf("  0");
                        }
                        prt_Hex(ird, 1);                // 16進数で送信(表示)
                        ird = 0;                        // 赤外データをクリア 
                        sbt = 1;                        // シフトバイトも初期化 
                        Sony = bct + 7;
                    }               
                }
            }
            printf("\r\n");
        }

        if(p_PW >= maxdata){                            // バッファ満杯なら
            printf("-- Buffer Full --\r\n");            //  Buffer Full 送信
        }
    }
}

/*****   End of File   *****/

※ 本プログラム作成にあたり、赤外線コード取得方法を参考にさせていただいた「初めてのPIC」さんに感謝。


☆☆☆ おまけ ☆☆☆
 LCD付き赤外線リモコン解読器(abc856)を改造し、SONYコードにも対応するようにした。
 改造に伴い変更したところは、
  ・RC2のスイッチ(入力)をLED(出力)に変更
  ・表示シフトプログラムを変更
  ・abc887のSONY信号解析部を追加
 ※ 解析プログラムにバグ(SONYコードの最後のバイトが表示されない)があり、修正しました。
 変更した abc856s2.c(zip)(修正済み)プログラム。


pin設定

SONYコード例(黄LED点灯)


※ 本レポートの参考・利用は、あくまでも自己責任でお願いします。


ペンシル型の電動ドライバー 赤外線リモコン(10)自動解読LCD表示