赤外線モーションセンサモジュール(HC-SR501)を試してみる(その3)


前回は赤外線モーションセンサモジュール(HC-SR501)を使って LEDを点灯させる方法を紹介しました。
LEDではつまらないので、今回はUSBライトを点灯させる方法を紹介します。
USBライトの点灯は「hub-ctrl」というプログラムを使用します。
「hub-ctrl」の使い方はこちらで紹介されています。

USBポートの電源を制御する

一応「hub-ctrl」のインストール方法を書いておきます。
$ mkdir hub-ctrl
$ cd hub-ctrl
$ wget http://www.gniibe.org/oitoite/ac-power-control-by-USB-hub/hub-ctrl.c
$ sudo apt-get install libusb-dev
$ gcc hub-ctrl.c -o hub-ctrl `pkg-config libusb --cflags --libs`

今回、使用したUSB-HUBはSANWA SUPPLYの「USB-HUB05TAN」という製品です。
このHUBは「Per-port power switching」機能を持っているので、「hub-ctrl」で電源ON/OFFを制御することができま す。

HUB05TAN
HUB05TAN

以下の写真のようにUSB-HUBに接続したまま、ライトのON/OFFが可能になります。




回路図は以下のとおりです。
プログラムは永久ループするので、一応プログラム終了のためのボタンを付けています。
ボタンの抵抗は10Kオームです。
右上にあるのが赤外線モーションセンサモジュールです。
赤外線モーションセンサモジュール(HC-SR501)の出力(黄色のワイヤー)はGPIO25につないでいます。

回路図

全体はこんな感じです。




ソースコードは以下のとおりです。
センサが人の動きを検出すると割り込みが発生し、子プロセスを起動します。
子プロセス起動時に、実行中の子プロセスの数を判定し、実行中の子プロセスが無ければ、USBの電源をONにします。
子プロセスが終了すると再度割り込みが発生します。
割り込み発生時に子プロセスの数を再度判定し、実行中の子プロセスが無くなれば、USBの電源をOFFにします。

以下の部分は環境に合わせて変更してください。
// Define Hub Control Command
#define PROG    "/home/pi/hub-ctrl/hub-ctrl" → hub-ctrlのフルパス

// Define Hub Control Chip
#define CHIP    "Texas" → USB-HUBに使われているチップメーカ

// Define Hub Port
#define PORT    4 → 使用するUSB-HUBのポート番号

USB-HUBに使われているチップメーカは以下で確認することができます。
$ sudo lsusb -v | grep Bus
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 006: ID 0451:1446 Texas Instruments, Inc. TUSB2040/2070 Hub 
→ USB-HUBに使われているチップメー カ

/*
cc -o test4 test4.c -lwiringPi
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <wiringPi.h>

// Define Senser Pin
#define SENSER  25    // rsp board pin:22

// Define Button Pin
#define BUTTON    23      // rsp board pin:16

// Define Timeout
#define WAITSEC    15      // タイムアウト時間

// Define Hub Control Command
#define PROG    "/home/pi/hub-ctrl/hub-ctrl"

// Define Hub Control Chip
#define CHIP    "Texas"

// Define Hub Port
#define PORT    4

int semid;
int gpioid=0;

//USB-HUBの情報取得
int GetHubInfo (char *cmdline, char *bus, char *device)
{
    FILE *fp;
    char buf[256];
    char *tok;
    int flag=0;

    if ( (fp=popen(cmdline,"r")) ==NULL) return(1);

    while(fgets(buf, sizeof(buf), fp) != NULL) {
      if (strncmp(buf,"Bus ",4) == 0) {
        tok=strtok( buf, " :" );
        while( tok != NULL ){
          if (flag == 1) {
            strcpy(bus,tok);
            flag=0;
          }
          if (flag == 2) {
            strcpy(device,tok);
            flag=0;
          }
          if (strcmp(tok,"Bus") == 0) flag=1;
          if (strcmp(tok,"Device") == 0) flag=2;
          tok = strtok( NULL, " :" );  /* 2回目以降 */
        }
      }
    }
    pclose(fp);

    return(0);
}

//USB-HUBの電源ON
int HubOn (char *bus, char *device, int port)
{
    char buf[256];
    struct stat st;

    if (stat(PROG,&st) != 0) return(1);
    sprintf(buf,"sudo %s -b %s -d %s -P %d -p 1",PROG,bus,device,port);
    return(system(buf));
}

//USB-HUBの電源OFF
int HubOff (char *bus, char *device, int port)
{
    char buf[256];
    struct stat st;

    if (stat(PROG,&st) != 0) return(1);
    sprintf(buf,"sudo %s -b %s -d %s -P %d -p 0",PROG,bus,device,port);
    return(system(buf));
}

//排他処理開始(ロック開始)
//自分よりも先にロックされていた場合にはロック解除されるまでスリープします
void lock(int semid){
    struct sembuf sb[1];
    sb[0].sem_num=0;
    sb[0].sem_op=-1;
    sb[0].sem_flg=0;
    if(EOF==semop(semid,sb,1)){
        printf("%s\n","lock ERR");
        exit(1);
    }
}

//排他処理終了(ロック解除)
void unlock(int semid){
    struct sembuf sb[1];
    sb[0].sem_num=0;
    sb[0].sem_op=1;
    sb[0].sem_flg=0;
    if(EOF==semop(semid,sb,1)){
        printf("%s\n","unlock ERR");
        exit(1);
    }
}

//セマフォ値のセット
void SemSetValue(int semid, int semnum, int semval) {
    union semun {
        int              val;     /* SETVAL の値 */
        struct semid_ds  *buf;    /* IPC_STAT, IPC_SET 用のバッファ */
        unsigned short   *array;  /* GETALL, SETALL 用の配列 */
    } ctl_arg;

    ctl_arg.val = semval;
    if((semctl(semid,semnum,SETVAL,ctl_arg))==EOF){
        printf("%s\n","semctl ERR");
        exit(1);
    }
}

//センサーが反応したときの対応
void signal1(void){
     gpioid=1;
     unlock(semid);
}

//ボタンが押されたときの対応
void signal9(void){
     gpioid=9;
     unlock(semid);
}

int main(int argc, char **argv) {
    int endFlag=1;
    pid_t result_pid;
    int status;
    int process_count=0;
    char cmdline[50];
    char bus[10];
    char device[10];
    int ret;

    sprintf(cmdline,"sudo lsusb -v | grep ^Bus | grep %s",CHIP);
    ret=GetHubInfo (cmdline, bus, device);

    //セマフォを割り付けます
    semid=semget(IPC_PRIVATE,1,0666);
    if(semid==EOF){
        printf("%s\n","semget ERR");
        exit(1);
    }

    //セマフォに初期値を書き込みます
    SemSetValue(semid,0,0);

    if (wiringPiSetupGpio() == -1){
        printf("wiringPiSetup init error\n");
        exit(1);
    }
    pinMode(SENSER, INPUT);
    pinMode(BUTTON, INPUT);
    HubOff (bus, device, PORT);

    //割り込みを設定します
    wiringPiISR( SENSER, INT_EDGE_FALLING, signal1 );
    wiringPiISR( BUTTON, INT_EDGE_FALLING, signal9 );

    while(endFlag){
        lock(semid); // Wait Unlock
        switch (gpioid) {
            case 0: // 子プロセス終了
                process_count--;
                if (process_count == 0) HubOff (bus, device, PORT);
                break;
            case 1: // センサからの入力あり
                if (process_count == 0) HubOn (bus, device, PORT);
                process_count++;
                result_pid = fork();
                if (result_pid == 0) { // 子プロセス開始
                    sleep(WAITSEC);
                    unlock(semid);
                    exit(0);
                }
                break;
            case 9: // ボタンが押された
                endFlag=0;
                break;
            default: //
                break;
        }
        gpioid=0;
    }

    if (process_count != 0) { // 子プロセス終了待ち
        waitpid( result_pid,&status,0);
    }
    HubOff (bus, device, PORT);

    //セマフォの削除
    if((semctl(semid,0,IPC_RMID,0))==EOF){
        printf("%s\n","semctl ERR");
        exit(1);
    }

    exit(0);
}

USBライトだけでなく、USB接続の扇風機やカップヒータなど、USB接続のデバイスであれば
何でも同じやり方で制御することができます。
次回はライトセンサーを追加します。