親指サイズの USB 赤外線リモコンが面白い
昨年末、調べごとをしていた時にちょっと気になる商品が目に留まりました。 株式会社ビット・トレード・ワン 様の「USB 接続 赤外線リモコン KIT」という製品です。
特徴をざっくりまとめてみるとこんな感じです。
PC から制御可能な学習型赤外線リモコンといえば 2006 年の発売以来ロングセラーを続ける PC-OP-RS1 がとても有名ですが、このキットは昨年(2012年)発売された製品とのことで、ソフトウェア要素の多くがオープンであることに魅力を感じました。一方で 公式フォーラムを覗いてみるとエアコンまわりをはじめいろいろ制約もあるようで、それをどう考えるかは価格とのバランスの解釈次第でしょう。そういった話題も含めて好奇心をそそられ、年明け早々に入手しました。画像は「フリスク」と並べてみた様子です。
- [パソコンから家庭用機器をリモコン操作]、[リモコンでパソコンを操作] の2つの機能を持つ
- 赤外線送信用のライブラリやツール・ファームウェアのソースコードが公開されている
- 家電協/ NEC/ SONY の各リモコンコードフォーマットに対応
- 某清涼菓子のケースにぴったり収まるサイズ
- キットは 1,680 円、組立ずみ製品でも 2,480 円と低価格
- PC 側対応 OS はWindows 7, Vista, XP
PC とは A−miniB タイプの USB ケーブルで接続。Windows 上でヒューマンインターフェイスデバイスとして認識され標準の HID ドライバが使用されます。
とりあえず使ってみると・・
さっそく専用の 送信用設定ツール に手元のいくつかの機器のリモコンコードを学習させ本キットから信号を出力し、それを元の機器が認識できるか否かを試してみると下の図の結果となりました。
シャープ製のアナログ TV は 1990年代のかなり旧いもので、セイテック製の照明器具用リモートコンセントは 10 年ほど前に近所のホームセンターで購入したものです。これらは NG でしたが、パナソニック製の DVD レコーダは問題なく扱うことができました。ちなみに前出の PC-OP-RS1 ではいずれも学習・再生に問題はなく、本キットで処理可能な送受信データの最大長が 6 バイトであることにも関係があるのかも知れません(PC-OP-RS1 のデータは 240 バイト)。メーカーへ尋ねたところ、赤外線リモコンの仕様は完全には規格化されておらずそれらのデータは本キットで正しく認識できていない可能性がある、とのことでした。
※「動作確認済リモコンリスト」(PDF 資料):メーカーサイトより
他のリモコン機器もいくつか試したところ、手元では 家電協フォーマット に準じたものは正しく扱うことができました。このフォーマットはもともと独自仕様の乱立していた日本製家電製品の赤外線リモコン規格を統一するために業界団体(財団法人家電製品協会)が策定した標準規格とのことで国産の多くのリモコン機器がこれに準拠しているようです。毎日あちこちで赤外線リモコンを使っていながら今まで中のしくみを意識するきっかけのなかった自分にとってこういった話題は新鮮で興味を感じます。本キットのファームウェアのソースコードは勉強や実験に大いに役に立ちそうです。
「赤外線送信ライブラリ」について
本キットの赤外線送信機能を自作のコードから利用するためのライブラリが公開されています。
メーカー側のこうした配慮は利用者にとって大変嬉しいもので製品そのものの魅力も大きく向上しますね。シンプルで分かり易い API 構成にも好感を持ちました。
ただ、ちょっと残念だったのはモジュールが .NET ライブラリだったことです。個人的な好みではありますが、今のところ私自身は .NET も .NET アプリケーションもあまり好きではないのです (^^; もちろん、たとえば複数のプラットフォームでの稼動を目的とする選択であればまた話は別なのですが、Win32 API に強く依存する内容であれば少なくともライブラリとしてはネイティブ形式のほうが間口が広い分便宜が大きいようにも思います。
そんなわけで、送信用設定ツールのコードをもとにリモコンデータ送受信用の処理を C 言語でできるだけ短くコンソールアプリケーションとして実装してみました。「赤外線送信ライブラリ」のソースは公開されていないようですが、必要があればこの CUI アプリのコードから同等のネイティブ DLL を作ることも可能ですね。
以下に、一連の作業の過程で知った PC−キット間の送受信手順と、作成したコードを控えます。
Mac でも動かせないか?(2013.01.29 追記)
Mac は普段あまり触らないのですが、このキットが Windows 上でヒューマンインターフェイスデバイス (HID) として認識され標準のドライバで駆動するということは Mac でもそのまま使えるのではないか?と興味を持ち情報を探してみました。
HID Class Device Interface Guide : USB HID Overview - developer.apple.com
OS X provides the HID Manager (described in “The HID Manager”) to support access to any devices that conform to the USB HID specification. While this is most commonly used for communicating with input devices, a number of other devices also use HID descriptors, and can thus be accessed using the same mechanism.
HID Class Device Interface Guide : Accessing a HID Device - developer.apple.com
やはりアプリケーションレイヤのコードからアクセスできそうなのでさっそくコードを試作してみました。あわせて掲載します。
PC →← キット間のデータ送受信手順
PC と本キットの間のデータ送受信には 65 バイト固定長の BYTE 配列を使用する。
デバイスオープン後の送受信手順を以下に示す。
リモコンデータの受信 1. 本キットをリモコンデータ受信待ちモードに [0] [1] [2] [3] [4] [61] [62] [63] [64] write┌──┬──┬──┬──┬──┬ ┬──┬──┬──┬──┐ →→ │0x00│0x51│0x01│0xFF│0xFF│・・・│0xFF│0xFF│0xFF│0xFF│ └──┴──┴──┴──┴──┴ ┴──┴──┴──┴──┘ [0] [1] [2] [3] [4] [61] [62] [63] [64] read ┌──┬──┬──┬──┬──┬ ┬──┬──┬──┬──┐ ←← │0x00│0x51│ ・・・ └──┴↑─┴──┴──┴──┴ ┴──┴──┴──┴──┘ OK 2. リモコンデータ受信 [0] [1] [2] [3] [4] [61] [62] [63] [64] write┌──┬──┬──┬──┬──┬ ┬──┬──┬──┬──┐ →→ │0x00│0x50│0xFF│0xFF│0xFF│・・・│0xFF│0xFF│0xFF│0xFF│ └──┴──┴──┴──┴──┴ ┴──┴──┴──┴──┘ [0] [1] [2] [3] [4] [5] [6] [7] [8] [63] [64] read ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬ ┬──┬──┐ ←← │0x00│0x50│0x??│0x??│0x??│0x??│0x??│0x??│0x??│・・・ └──┴↑─┴↑─┴──┴──┴──┴──┴──┴──┴ ┴──┴──┘ OK !=0x00 └──認識した7バイトのリモコンコード──┘ 3. リモコンデータ受信待ちモード解除 [0] [1] [2] [3] [4] [61] [62] [63] [64] write┌──┬──┬──┬──┬──┬ ┬──┬──┬──┬──┐ →→ │0x00│0x51│0x00│0xFF│0xFF│・・・│0xFF│0xFF│0xFF│0xFF│ └──┴──┴──┴──┴──┴ ┴──┴──┴──┴──┘ [0] [1] [2] [3] [4] [61] [62] [63] [64] read ┌──┬──┬──┬──┬──┬ ┬──┬──┬──┬──┐ ←← │0x00│0x51│ ・・・ └──┴↑─┴──┴──┴──┴ ┴──┴──┴──┴──┘ OK
リモコンデータの送信 [0] [1] [2] [3] [4] [5] [6] [7] [8] [63] [64] write┌──┬──┬──┬──┬──┬──┬──┬──┬──┬ ┬──┬──┐ →→ │0x00│0x60│0x??│0x??│0x??│0x??│0x??│0x??│0x??│・・・│0xFF│0xFF│ └──┴──┴──┴──┴──┴──┴──┴──┴──┴ ┴──┴──┘ └─── 7バイトのリモコンコード────┘
C 言語によるデータ送受信実装例
Windows 用
C:\temp>BtoIrRemocon.exe
waiting...
C10220B00000B0 ←キットが受信・認識したリモコンコード
C:\temp>BtoIrRemocon.exe C10220B00000B0 p2 C10220B00000B2
[C10220B00000B0]
[p2] ←複数のコードを指定可,pN = N 秒間のポーズ
[C10220B00000B2]
/** * * BtoIrRemocon.c * * 2013 KLab Inc. * * ビット・トレード・ワン社製「USB 接続赤外線リモコンキット」を操作する * Windows コンソールプログラム * * 同社製品ページ: * http://bit-trade-one.co.jp/BTOpicture/Products/005-RS/index.html * * This software is provided "as is" without any express and implied warranty * of any kind. The entire risk of the quality and performance of this software * with you, and you shall use this software your own sole judgment and * responsibility. KLab shall not undertake responsibility or liability for * any and all damages resulting from your use of this software. * KLab does not warrant this software to be free from bug or error in * programming and other defect or fit for a particular purpose, and KLab does * not warrant the completeness, accuracy and reliability and other warranty * of any kind with respect to result of your use of this software. * KLab shall not be obligated to support, update or upgrade this software. * */ /* 使い方: 0. PC に USB 接続赤外線リモコンキット(以下"USB IR REMOCON")を接続 1.リモコンデータ受信モード BtoIrRemocon.exe を引数なしで実行するとリモコンデータ受信モードで起動する。 手持ちの赤外線リモコンから USB IR REMOCON の赤外線 LED に向けて赤外線 データを送るとコンソールに 14 文字のリモコンデータコードが表示される。 例:C10220B0008030 2.リモコンデータ送信モード BtoIrRemocon.exe を上記 1. のコードを引数として起動すると USB IR REMOCON の赤外線 LED からそのリモコンデータが照射される。コードは複数指定可能、 途中に p<秒数> の要領でポーズ指定を記述できる。 例:BtoIrRemocon C10220B0008030 p3 C10220B0008034 p3 C10220B0008036 */ #include <windows.h> #include <stdio.h> #include <setupapi.h> #pragma comment(lib, "setupapi") #define RECEIVE_WAIT_MODE_NONE 0 #define RECEIVE_WAIT_MODE_WAIT 1 #define DEVICE_BUFSIZE 65 #define REMOCON_DATA_LENGTH 7 typedef void (__stdcall *DEF_HidD_GetHidGuid)(LPGUID HidGuid); // PC に接続ずみの「USB 接続赤外線リモコンキット」のデバイスパスを取得 DWORD GetDevicePath(OUT char *pszDevicePath, IN DWORD cchBuf) { // USB IR REMOCON 固有の ID 文字列 char *szIdStr1 = "vid_22ea&pid_001e"; char *szIdStr2 = "mi_03"; int i; char *pszProp; HDEVINFO DeviceInfoTable = NULL; SP_DEVICE_INTERFACE_DATA DeviceIfData; PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceIfDetailData; SP_DEVINFO_DATA DevInfoData; DWORD InterfaceIndex, dwSize, dwError = ERROR_SUCCESS; HGLOBAL hMem = NULL; BOOL bFound; GUID InterfaceClassGuid; HMODULE hHidDLL; DEF_HidD_GetHidGuid pHidD_GetHidGuid; size_t len; //GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}; // HIDClass の GUID を取得 hHidDLL = LoadLibrary("hid.dll"); pHidD_GetHidGuid = (DEF_HidD_GetHidGuid)GetProcAddress(hHidDLL, "HidD_GetHidGuid"); pHidD_GetHidGuid(&InterfaceClassGuid); FreeLibrary(hHidDLL); // HIDClass に属するデバイス群の含まれるデバイス情報セットを取得 DeviceInfoTable = SetupDiGetClassDevs(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if(!DeviceInfoTable) { dwError = GetLastError(); goto DONE; } // デバイス情報セットを走査し IR REMOCON デバイスを探す for (InterfaceIndex = 0; InterfaceIndex < 10000000; InterfaceIndex++) { DeviceIfData.cbSize = sizeof(DeviceIfData); if(SetupDiEnumDeviceInterfaces(DeviceInfoTable, NULL, &InterfaceClassGuid, InterfaceIndex, &DeviceIfData)) { dwError = GetLastError(); if (dwError == ERROR_NO_MORE_ITEMS) { goto DONE; } } else { dwError = GetLastError(); goto DONE; } // 現在見ているデバイスの VID, PID の含まれるハードウェア ID 文字列を取得 DevInfoData.cbSize = sizeof(DevInfoData); SetupDiEnumDeviceInfo(DeviceInfoTable, InterfaceIndex, &DevInfoData); SetupDiGetDeviceRegistryProperty(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, NULL, NULL, 0, &dwSize); hMem = GlobalAlloc(0, dwSize); if (!hMem) { dwError = GetLastError(); goto DONE; } SetupDiGetDeviceRegistryProperty(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, NULL, (PBYTE)hMem, dwSize, NULL); pszProp = strdup((char*)hMem); GlobalFree(hMem); hMem = NULL; // 取得したハードウェア ID 文字列に USB IR REMOCON 固有の VID, PID が含まれるか len = strlen(pszProp); for (i = 0; i < (int)len; i++) { pszProp[i] = tolower(pszProp[i]); } bFound = FALSE; if (strstr(pszProp, szIdStr1) != NULL && strstr(pszProp, szIdStr2) != NULL) { bFound = TRUE; } free(pszProp); // USB IR REMOCON 発見 if (bFound) { // USB IR REMOCON のデバイスパスを得る SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, &DeviceIfData, NULL, 0, &dwSize, NULL); hMem = GlobalAlloc(0, dwSize); if (!hMem) { dwError = GetLastError(); goto DONE; } pDeviceIfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)hMem; pDeviceIfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if (SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, &DeviceIfData, pDeviceIfDetailData, dwSize, NULL, NULL)) { if (cchBuf > dwSize - sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)) { // 成功 strcpy(pszDevicePath, pDeviceIfDetailData->DevicePath); dwError = ERROR_SUCCESS; } else { dwError = ERROR_NOT_ENOUGH_MEMORY; goto DONE; } goto DONE; } else { dwError = GetLastError(); goto DONE; } } } DONE: if (hMem) { GlobalFree(hMem); } if (DeviceInfoTable) { SetupDiDestroyDeviceInfoList(DeviceInfoTable); } return dwError; } // リモコンデータ送信モード int Transfer(HANDLE hDevice, int ac, char **av) { int i, j, sts = -1; DWORD len; BYTE buf[DEVICE_BUFSIZE]; char *pt, *e, s[3] = "\0\0"; memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x60; // 送信指定 // パラメータを順に走査して処理 for (i = 1; i < ac; i++) { pt = av[i]; fprintf(stderr, "[%s]\n", pt); // "pN" => N 秒間ポーズ if (tolower(*pt) == 'p') { if (pt[1] != '\0' && isdigit(pt[1])) { Sleep(1000 * atoi(&pt[1])); continue; } else { fprintf(stderr, "[%s] is skipped..\n", pt); continue; } } else if (strlen(pt) != REMOCON_DATA_LENGTH * 2) { continue; } // 16 進文字列をバイト配列へ for (j = 0; j < REMOCON_DATA_LENGTH; j++) { s[0] = pt[j*2]; s[1] = pt[j*2+1]; buf[j+2] = (BYTE)strtoul(s, &e, 16); if (*e != '\0') { // 変換エラー break; } } if (j != REMOCON_DATA_LENGTH) { fprintf(stderr, "[%s] is skipped..\n", pt); continue; } // 1件のリモコンデータを送信 if (!WriteFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL)) { fprintf(stderr, "WriteFile: err=%u\n", GetLastError()); goto DONE; } } memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x40; // デバイスの送信バッファをクリア WriteFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL); memset(buf, 0x00, sizeof(buf)); ReadFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL); sts = 0; DONE: return sts; } // リモコンデータ受信モード int Display(HANDLE hDevice) { int i, sts = -1; DWORD len; BYTE buf[DEVICE_BUFSIZE]; // デバイスの送信バッファをクリア memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x40; WriteFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL); memset(buf, 0x00, sizeof(buf)); ReadFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL); fprintf(stderr, "waiting...\n"); // デバイスをリモコンデータ受信待ちモードに memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x51; buf[2] = RECEIVE_WAIT_MODE_WAIT; if (!WriteFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL)) { fprintf(stderr, "WriteFile: err=%u\n", GetLastError()); goto DONE; } memset(buf, 0x00, sizeof(buf)); if (!ReadFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL)) { fprintf(stderr, "ReadFile: err=%u\n", GetLastError()); goto DONE; } if (buf[1] != 0x51) { fprintf(stderr, "invalid response"); goto DONE; } // リモコンデータ受信待ち while (TRUE) { memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x50; WriteFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL); memset(buf, 0x00, sizeof(buf)); ReadFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL); if (buf[1] == 0x50 && buf[2] != 0) { // 受信データありなら 16 進表示 for (i = 0; i < 7; i++) { printf("%02X", buf[i+2]); } putchar('\n'); break; } Sleep(500); } // リモコンデータ受信待ちモードを解除 memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x51; buf[2] = RECEIVE_WAIT_MODE_NONE; WriteFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL); memset(buf, 0x00, sizeof(buf)); ReadFile(hDevice, buf, DEVICE_BUFSIZE, &len, NULL); sts = 0; DONE: return sts; } // エントリーポイント int main(int ac, char **av) { int sts = -1; char szDevicePath[256]; HANDLE hDevice = INVALID_HANDLE_VALUE; BOOL doDisplay = FALSE; DWORD dwError; // 引数無しなら受信モード if (ac < 2) { doDisplay = TRUE; } // USB IR REMOCON のデバイスパスを得る dwError = GetDevicePath(szDevicePath, sizeof(szDevicePath)); if (dwError != ERROR_SUCCESS) { fprintf(stderr, "failed to get device path: err=%u", dwError); goto DONE; } // データ送受信用にデバイスをオープン hDevice = CreateFile(szDevicePath, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDevice == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); fprintf(stderr, "CreateFile: err=%u\n", err); goto DONE; } if (doDisplay) { sts = Display(hDevice); // 受信モードへ } else { sts = Transfer(hDevice, ac, av); // 送信モードへ } DONE: if (hDevice != INVALID_HANDLE_VALUE) { CloseHandle(hDevice); } return sts; }
Mac OS X 用
$ BtoIrRemoconMac
waiting...
C10220B00000B0 ←キットが受信・認識したリモコンコード
$ BtoIrRemoconMac C10220B00000B0 p2 C10220B00000B2
[C10220B00000B0]
[p2] ←複数のコードを指定可,pN = N 秒間のポーズ
[C10220B00000B2]
/** * * BtoIrRemoconMac.c * * 2013 KLab Inc. * * ビット・トレード・ワン社製「USB 接続赤外線リモコンキット」を操作する * Mac OS X 用 コンソールプログラム * * 同社製品ページ: * http://bit-trade-one.co.jp/BTOpicture/Products/005-RS/index.html * * ビルド方法: * gcc -Wall -g BtoIrRemoconMac.c -framework IOKit -framework CoreFoundation -o BtoIrRemoconMac * - gcc 4.2.1 build 5666 (Xcode 3.2.6) でのビルドを確認 * * HID Class Device Interfaces Guide - developer.apple.com * http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/HID/intro/intro.html * * * This software is provided "as is" without any express and implied warranty * of any kind. The entire risk of the quality and performance of this software * with you, and you shall use this software your own sole judgment and * responsibility. KLab shall not undertake responsibility or liability for * any and all damages resulting from your use of this software. * KLab does not warrant this software to be free from bug or error in * programming and other defect or fit for a particular purpose, and KLab does * not warrant the completeness, accuracy and reliability and other warranty * of any kind with respect to result of your use of this software. * KLab shall not be obligated to support, update or upgrade this software. * */ /* 使い方: 0. PC に USB 接続赤外線リモコンキット(以下"USB IR REMOCON")を接続 1.リモコンデータ受信モード BtoIrRemoconMac を引数なしで実行するとリモコンデータ受信モードで起動する。 手持ちの赤外線リモコンから USB IR REMOCON の赤外線 LED に向けて赤外線 データを送るとコンソールに 14 文字のリモコンデータコードが表示される。 例:C10220B0008030 2.リモコンデータ送信モード BtoIrRemoconMac を上記 1. のコードを引数として起動すると USB IR REMOCON の赤外線 LED からそのリモコンデータが照射される。コードは複数指定可能、 途中に p<秒数> の要領でポーズ指定を記述できる。 例:BtoIrRemoconMac C10220B0008030 p3 C10220B0008034 p3 C10220B0008036 */ #include <stdio.h> #include <unistd.h> #include <IOKit/hid/IOHIDManager.h> #include <IOKit/hid/IOHIDKeys.h> #include <CoreFoundation/CoreFoundation.h> #define RECEIVE_WAIT_MODE_NONE 0 #define RECEIVE_WAIT_MODE_WAIT 1 #define DEVICE_BUFSIZE 65 #define REMOCON_DATA_LENGTH 7 static int g_readBytes; // 指定されたキーの整数プロパティを取得 int getIntProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey) { int val; if (inIOHIDDeviceRef) { CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); if (tCFTypeRef) { if (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) { if (!CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, &val)) { val = -1; } } } } return val; } // レポートのコールバック関数 static void reportCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDReportType inType, uint32_t inReportID, uint8_t *inReport, CFIndex InReportLength) { g_readBytes = InReportLength; } // デバイスからの読み込み int ReadFromeDevice(IOHIDDeviceRef dev, unsigned char *buf, size_t bufsize, CFTimeInterval timeoutSecs) { IOHIDDeviceRegisterInputReportCallback(dev, &buf[1], bufsize-1, reportCallback, NULL); g_readBytes = -1; CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeoutSecs, false); //printf("ReadFromeDevice: len=%d, 0=%X 1=%X 2=%X\n", g_readBytes, buf[0], buf[1], buf[2]); return g_readBytes; } // デバイスへの書き込み IOReturn WriteToDevice(IOHIDDeviceRef dev, unsigned char *data, size_t len) { IOReturn ret = IOHIDDeviceSetReport(dev, kIOHIDReportTypeOutput, data[0], data+1, len-1); if (ret != kIOReturnSuccess) { //printf("WriteToDevice: ret=0x%08X\n", ret); } return ret; } // リモコンデータ送信モード int Transfer(IOHIDDeviceRef refDevice, int ac, char **av) { int i, j, sts = -1; unsigned char buf[DEVICE_BUFSIZE]; char *pt, *e, s[3] = "\0\0"; memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x60; // 送信指定 // パラメータを順に走査して処理 for (i = 1; i < ac; i++) { pt = av[i]; fprintf(stderr, "[%s]\n", pt); // "pN" => N 秒間ポーズ if (tolower(*pt) == 'p') { if (pt[1] != '\0' && isdigit(pt[1])) { sleep(atoi(&pt[1])); continue; } else { fprintf(stderr, "[%s] is skipped..\n", pt); continue; } } else if (strlen(pt) != REMOCON_DATA_LENGTH * 2) { continue; } // 16 進文字列をバイト配列へ for (j = 0; j < REMOCON_DATA_LENGTH; j++) { s[0] = pt[j*2]; s[1] = pt[j*2+1]; buf[j+2] = (unsigned char)strtoul(s, &e, 16); if (*e != '\0') { // 変換エラー break; } } if (j != REMOCON_DATA_LENGTH) { fprintf(stderr, "[%s] is skipped..\n", pt); continue; } // 1件のリモコンデータを送信 if (WriteToDevice(refDevice, buf, DEVICE_BUFSIZE) != kIOReturnSuccess) { fprintf(stderr, "WriteToDevice: err\n"); goto DONE; } } memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x40; // デバイスの送信バッファをクリア WriteToDevice(refDevice, buf, DEVICE_BUFSIZE); memset(buf, 0x00, sizeof(buf)); ReadFromeDevice(refDevice, buf, DEVICE_BUFSIZE, 0.5); sts = 0; DONE: return sts; } // リモコンデータ受信モード int Display(IOHIDDeviceRef refDevice) { int i, sts = -1; unsigned char buf[DEVICE_BUFSIZE]; // デバイスの送信バッファをクリア memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x40; WriteToDevice(refDevice, buf, DEVICE_BUFSIZE); memset(buf, 0x00, sizeof(buf)); ReadFromeDevice(refDevice, buf, DEVICE_BUFSIZE, 0.5); fprintf(stderr, "waiting...\n"); // デバイスをリモコンデータ受信待ちモードに memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x51; buf[2] = RECEIVE_WAIT_MODE_WAIT; if (WriteToDevice(refDevice, buf, DEVICE_BUFSIZE) != kIOReturnSuccess) { fprintf(stderr, "WriteToDevice: err\n"); goto DONE; } memset(buf, 0x00, sizeof(buf)); if (ReadFromeDevice(refDevice, buf, DEVICE_BUFSIZE, 0.5) < 0) { fprintf(stderr, "ReadFromeDevice: err\n"); goto DONE; } if (buf[1] != 0x51) { fprintf(stderr, "invalid response"); goto DONE; } // リモコンデータ受信待ち while (true) { memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x50; WriteToDevice(refDevice, buf, DEVICE_BUFSIZE); memset(buf, 0x00, sizeof(buf)); ReadFromeDevice(refDevice, buf, DEVICE_BUFSIZE, 0.5); if (buf[1] == 0x50 && buf[2] != 0) { // 受信データありなら 16 進表示 for (i = 0; i < 7; i++) { printf("%02X", buf[i+2]); } putchar('\n'); break; } } // リモコンデータ受信待ちモードを解除 memset(buf, 0xFF, sizeof(buf)); buf[0] = 0; buf[1] = 0x51; buf[2] = RECEIVE_WAIT_MODE_NONE; WriteToDevice(refDevice, buf, DEVICE_BUFSIZE); memset(buf, 0x00, sizeof(buf)); ReadFromeDevice(refDevice, buf, DEVICE_BUFSIZE, 0.5); sts = 0; DONE: return sts; } int main(int ac, char *av[]) { int vid, myVID = 0x22ea; // BTO IR REMOCON のベンダ ID int pid, myPID = 0x001e; // BTO IR REMOCON のプロダクト ID int i, sts = -1; IOReturn ret; unsigned char buf[65]; Boolean doDisplay = false; IOHIDManagerRef refHidMgr = NULL; IOHIDDeviceRef refDevice, *prefDevs = NULL; CFSetRef refDevSet = NULL; CFIndex numDevices; // 引数無しなら受信モード if (ac < 2) { doDisplay = true; } // HID マネージャリファレンスを生成 refHidMgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); // すべての HID デバイスを対象とする IOHIDManagerSetDeviceMatching(refHidMgr, NULL); IOHIDManagerScheduleWithRunLoop(refHidMgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); // HID マネージャを開く IOHIDManagerOpen(refHidMgr, kIOHIDOptionsTypeNone); // マッチしたデバイス群のセットを得る refDevSet = IOHIDManagerCopyDevices(refHidMgr); numDevices = CFSetGetCount(refDevSet); prefDevs = malloc(numDevices * sizeof(IOHIDDeviceRef)); // セットから値を取得 CFSetGetValues(refDevSet, (const void **)prefDevs); // HID デバイス群を走査して BTO IR REMOCON を探す for (i = 0; i < numDevices; i++) { refDevice = prefDevs[i]; // VID, PID をチェック vid = getIntProperty(refDevice, CFSTR(kIOHIDVendorIDKey)); pid = getIntProperty(refDevice, CFSTR(kIOHIDProductIDKey)); if (vid != myVID || pid != myPID) { refDevice = NULL; continue; } // デバイスのオープン ret = IOHIDDeviceOpen(refDevice, kIOHIDOptionsTypeNone); if (ret != kIOReturnSuccess) { refDevice = NULL; continue; } // 試し打ち memset(buf, 0xFF, sizeof(buf)); buf[0] = 0x00; buf[1] = 0x40; if (WriteToDevice(refDevice, buf, DEVICE_BUFSIZE) == kIOReturnSuccess) { memset(buf, 0, sizeof(buf)); int bytes = ReadFromeDevice(refDevice, buf, DEVICE_BUFSIZE, 0.5); if (bytes >= 0 && buf[1] == 0x40) { break; // OK } } IOHIDDeviceClose(refDevice, kIOHIDOptionsTypeNone); refDevice = NULL; } if (!refDevice) { fprintf(stderr, "device not found\n"); goto DONE; } if (doDisplay) { sts = Display(refDevice); // 受信モードへ } else { sts = Transfer(refDevice, ac, av); // 送信モードへ } IOHIDDeviceClose(refDevice, kIOHIDOptionsTypeNone); DONE: if (prefDevs) { free(prefDevs); } if (refDevSet) { CFRelease(refDevSet); } if (refHidMgr) { IOHIDManagerClose(refHidMgr, kIOHIDOptionsTypeNone); CFRelease(refHidMgr); } return sts; }
(tanabe)
この記事へのコメント
HIDのコード参考になりました。こちらも
赤外線リモコン関係をいろいろいじってい
ますのでで、見てやってください。
じっくり拝見します
参考になりました。
新しいのも出たようですね。
ttp://www.omiya-giken.com/?page_id=837
まぁまぁのようです。
ttp://cubic9.com/Devel/%C5%C5%BB%D2%B9%A9%BA%EE/irMagician/
yosemiteのせいか、うまく動くとき動かない時のばらつきがありました。
よくわかっていないのですが、VID,PIDのチェックのところで、kIOHIDPrimaryUsagePageKeyもチェックするとうまくいくみたいです。
getIntProperty(refDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
の戻り値が、0xFF00の以外の時をcontinueの条件に追加しました。
送信、受信とも止まることなく動くようになりました。
以下のソースの差分をほぼ流用してちょっと追加しただけです。
https://github.com/Velmy/BtoIrRemocon/tree/vs2010
$ diff -w BtoIrRemocon.c BtoIrRemoconMac/BtoIrRemoconMac/BtoIrRemocon.c
63c63
< #define REMOCON_DATA_LENGTH 7
---
> #define REMOCON_DATA_LENGTH (1 + 2 + 32)
124c124
< buf[1] = 0x60; // 送信指定
---
> buf[1] = 0x61; // 送信指定
192c192
< buf[1] = 0x51;
---
> buf[1] = 0x53;
203c203
< if (buf[1] != 0x51) {
---
> if (buf[1] != 0x53) {
211c211
< buf[1] = 0x50;
---
> buf[1] = 0x52;
215c215
< if (buf[1] == 0x50 && buf[2] != 0) {
---
> if (buf[1] == 0x52 && buf[2] != 0) {
217c217
< for (i = 0; i < 7; i++) {
---
> for (i = 0; i < REMOCON_DATA_LENGTH; i++) {
240a241,242
> int uPage, myUPage = 0xFF00; // Usage Page = Microsoft
> int usage, myUsage = 0x01; // Usage = Base Up
278a281,286
> usage = getIntProperty(refDevice, CFSTR(kIOHIDPrimaryUsageKey));
> uPage = getIntProperty(refDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
> if (uPage != myUPage || usage != myUsage) {
> refDevice = NULL;
> continue;
> }
$
ありがとうございました。
この形式今でも使えるのでしょうか?
こちらのosはWindows10です。どうしてもシャープレコーダーhddの換装したいのでぜひ教えていただけないでしょうか?
>ビルト済実行形式をダウンロードしたのですが解凍できず
手元の環境(非 Windows10)では解凍そのものに支障は見受けられないのですが
ダウンロードされたアーカイブに問題はないでしょうか。まずその点をご確認下さい。
Windows10 での動作は未確認です。ご覧のようにソースコードをすべて公開していますので、
もし何か支障がありましたらお手元で改修の上実行形式をビルドしてお試し下さい。
コンソールアプリケーションにあまり馴染みのない場合は
メーカー様公式の GUI アプリのほうが扱いやすいと思います。
良い展開をお祈りします。
sharpのBDレコーダーのHDDが不具合が生じHDD換装をしましたがsharpサービスマンコードがないとHddの認識ができず困っていろいろググって得た結果、最もPCからの赤外線リモコンのコードの照射を行うに適していたのが開発者さんの(ビルド実行ずみ形式)をDLしバッチ「BtolrRemocon.exe C1AA5A8F30F501]をbat fileにし実行するとBittrerdwann リモコンから赤外線が照射されているのが確認されましたが問題のカラーバーが表示されません。これを解消するアドバイスございませんか。?ご指導お願いいたします。
何度か内容を読み返しましたが、こちらへ質問されることが適切な話題ではないようです(※あえて率直に申し上げれば、こちらには何の話かわからずなぜここでこのような質問をされるのかとても不思議です)。機器の故障についてはそのメーカーさんへ相談して下さい。良い展開をお祈りします。
もしよろしければ、改変版のソースを公開しても構わないでしょうか。
チャンネルAA5A8F1211A1音AA5A8F1215E1も動作していますC言語はまだ初心者です
今のところは(新ファームが標準コマンドに対応していないので)全部拡張に書き直してx64でビルドしています。
オカリナさん
ビット長とデータタイプ指定が無いです。見たところアクオスのコードの様ですので”C1”を先頭に付けてみてください(電源なら「C1AA5A8F1216D1」です)
家電基準とNEC SONY 三菱がありますがこれ以外のヘッダーとデータタイプは可能でしょうかSONYはデーター波形が反転?
三菱はヘッダーが見当たらない?
おおよそですがヘッダー9ms 5ms(計14ms)ですデータは1=2.2 0=1.1 位です
リモコンLCDTV GA073WJSA
三菱のフォーマット使用で 3回発砲
C122 215D C122 ビット数は各16ですが
15ビットに変更して 電源ON OFF動作いたしました
i_ret = USBIR.writeUSBIRex(handle_usb_device, USBIR.IR_FORMAT.MITSUBISHI, code1, 15, 0)
参考にさせていただいております。
BtoIrRemocon.zipを解凍し、BtoIrRemocon.exeを立ち上げるとDOSウィンドウの表示が
waiting...
のみと なり切半角英数のキーは一切受け付けません。(ひらがなは表示するが確定すると消え、waiting...の状態に戻ります。)
当然ながらフリスクリモコンはUSB接続されており、REMOCON_CT_TRANSを用いて(電源on/offのみですが)リモコンコードの記録&送信は正常に行えます。
使用PCはsony VAIO でWindows7から無償アップロードしたWindous10と、
東芝DynabookでWindows8
で試しましたが同様でした。
何か解決策がありましたら ご教授ください。
> BtoIrRemocon.zipを解凍し、BtoIrRemocon.exeを立ち上げるとDOSウィンドウの表示が
> waiting...
> のみと なり切半角英数のキーは一切受け付けません。
これは仕様どおりの所作です。記事↓をいま一度ご確認下さい。
http://dsas.blog.klab.org/archives/52097996.html#win
|引数なしで実行するとリモコンデータ受信モードで起動
|
|C:\temp>BtoIrRemocon.exe
|waiting...
|C10220B00000B0 ←キットが受信・認識したリモコンコード
|
|リモコンコードを引数に指定して起動するとキットからそれを送出する
|
|C:\temp>BtoIrRemocon.exe C10220B00000B0 p2 C10220B00000B2
|[C10220B00000B0]
|[p2] ←複数のコードを指定可,pN = N 秒間のポーズ
|[C10220B00000B2]
この記事への他のかたからの過去のコメントもご参考になろうかと思います。
それでは、良い展開をお祈りします。