Arduino UNOでPCをスリープ

Arduino UNOを使って、ある条件の時にPCをスリープさせたいと思いました。 しかし、どうやらキーボード用のファームウェア(Arduino-keyboard.hex)を焼いてもスリープのコマンドは送れないらしい。

なので自分でファームウェアを書き換えて作ってみた。 今回はとりあえずスリープさえ出来ればいいと思って作ったので、キーボードと併用とかはできないです。

準備

必要なパッケージ

以下のものをaptでインストールします。

  • avr-libc(avr-gccも一緒にインストールされます)
  • dfu-programmer

ファームウェア書き換え

ファームウェア一式をダウンロードし、4箇所書き換えます。 今回はキーボード用のファームウェアをベースに書き換えます。

LUFA/Drivers/USB/Class/Common/HID.h

レポートデータの構造体を編集します。

               typedef struct
               {
                       uint8_t Modifier; /**< Keyboard modifier byte, indicating pressed modifier keys (a combination of
                                          *   HID_KEYBOARD_MODIFER_* masks).
                                          */
                       uint8_t Reserved; /**< Reserved for OEM use, always set to 0. */
                       uint8_t KeyCode[6]; /**< Key codes of the currently pressed keys. */
               } USB_KeyboardReport_Data_t;

               typedef struct
               {
                       uint8_t ReportId;
                       uint8_t Data;
               } USB_KeyboardReport_Data_t;

firmwares/arduino-keyboard/Arduino-keyboard.c

レポートデータのサイズが2になったので、82に変更します。

uint8_t keyboardData[8] = { 0 };

uint8_t keyboardData[2] = { 0 };

       if (BufferCount >= 8) {
           for (ind=0; ind<8; ind++) {
               keyboardData[ind] = RingBuffer_Remove(&USARTtoUSB_Buffer);
           }

       if (BufferCount &>= 2) {
           for (ind=0; ind<2; ind++) {
               keyboardData[ind] = RingBuffer_Remove(&USARTtoUSB_Buffer);
           }

           Serial_TxByte(ledReport);
       }

       for (ind=0; ind<8; ind++) {
           datap[ind] = keyboardData[ind];
       }

           Serial_TxByte(ledReport);
       }

       for (ind=0; ind<2; ind++) {
           datap[ind] = keyboardData[ind];
       }

firmwares/arduino-keyboard/Descriptors.c

レポートディスクリプタを書き換えます。

const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] =
{
       0x05, 0x01,          /* Usage Page (Generic Desktop)                    */
       0x09, 0x06,          /* Usage (Keyboard)                                */
       0xa1, 0x01,          /* Collection (Application)                        */
       0x75, 0x01,          /*   Report Size (1)                               */
       0x95, 0x08,          /*   Report Count (8)                              */
       0x05, 0x07,          /*   Usage Page (Key Codes)                        */
       0x19, 0xe0,          /*   Usage Minimum (Keyboard LeftControl)          */
       0x29, 0xe7,          /*   Usage Maximum (Keyboard Right GUI)            */
       0x15, 0x00,          /*   Logical Minimum (0)                           */
       0x25, 0x01,          /*   Logical Maximum (1)                           */
       0x81, 0x02,          /*   Input (Data, Variable, Absolute)              */
       0x95, 0x01,          /*   Report Count (1)                              */
       0x75, 0x08,          /*   Report Size (8)                               */
       0x81, 0x03,          /*   Input (Const, Variable, Absolute)             */
       0x95, 0x05,          /*   Report Count (5)                              */
       0x75, 0x01,          /*   Report Size (1)                               */
       0x05, 0x08,          /*   Usage Page (LEDs)                             */
       0x19, 0x01,          /*   Usage Minimum (Num Lock)                      */
       0x29, 0x05,          /*   Usage Maximum (Kana)                          */
       0x91, 0x02,          /*   Output (Data, Variable, Absolute)             */
       0x95, 0x01,          /*   Report Count (1)                              */
       0x75, 0x03,          /*   Report Size (3)                               */
       0x91, 0x03,          /*   Output (Const, Variable, Absolute)            */
       0x95, 0x06,          /*   Report Count (6)                              */
       0x75, 0x08,          /*   Report Size (8)                               */
       0x15, 0x00,          /*   Logical Minimum (0)                           */
       0x26, 231, 0,        /*   Logical Maximum (231)                         */
       0x05, 0x07,          /*   Usage Page (Keyboard)                         */
       0x19, 0x00,          /*   Usage Minimum (Reserved (no event indicated)) */
       0x29, 231,           /*   Usage Maximum (Keyboard Application)          */
       0x81, 0x00,          /*   Input (Data, Array, Absolute)                 */
       0xc0                 /* End Collection                                  */
};

const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] =
{
       0x05, 0x0c, // USAGE_PAGE (Consumer Devices)
       0x09, 0x01, // USAGE (Consumer Control)
       0xa1, 0x01, // COLLECTION (Application)
       0x85, 0x01, // REPORT_ID (1)
       0x15, 0x00, // LOGICAL_MINIMUM (0)
       0x25, 0x01, // LOGICAL_MAXIMUM (1)
       0x75, 0x01, // REPORT_SIZE (1)
       0x95, 0x10, // REPORT_COUNT (16)
       0x09, 0xe2, // USAGE (Mute) 0x01
       0x09, 0xe9, // USAGE (Volume Up) 0x02
       0x09, 0xea, // USAGE (Volume Down) 0x03
       0x09, 0xcd, // USAGE (Play/Pause) 0x04
       0x09, 0xb7, // USAGE (Stop) 0x05
       0x09, 0xb6, // USAGE (Scan Previous Track) 0x06
       0x09, 0xb5, // USAGE (Scan Next Track) 0x07
       0x0a, 0x8a, 0x01, // USAGE (Mail) 0x08
       0x0a, 0x92, 0x01, // USAGE (Calculator) 0x09
       0x0a, 0x21, 0x02, // USAGE (www search) 0x0a
       0x0a, 0x23, 0x02, // USAGE (www home) 0x0b
       0x0a, 0x2a, 0x02, // USAGE (www favorites) 0x0c
       0x0a, 0x27, 0x02, // USAGE (www refresh) 0x0d
       0x0a, 0x26, 0x02, // USAGE (www stop) 0x0e
       0x0a, 0x25, 0x02, // USAGE (www forward) 0x0f
       0x0a, 0x24, 0x02, // USAGE (www back) 0x10
       0x81, 0x62, // INPUT (Data,Var,Abs,NPrf,Null)
       0xc0,
       // System Control Descriptor
       0x05, 0x01, /* Usage Page (Generic Desktop) */
       0x09, 0x80, /* Usage (System Control) */
       0xA1, 0x01, /* Collection (Application) */
       0x85, 0x02, /* Report ID 0x02 [SYSTEM CTRL] */
       0x19, 0x82, /* Usage minimum (System Sleep) */
       0x29, 0x83, /* Usage maximum (System Wake up) */
       0x95, 0x02, /* Report count (2) */
       0x81, 0x06, /*Input (data, variable, relative, Preferred) */
       0x95, 0x06, /* Report count (6) */
       0x81, 0x01, /*Input (Constant) */
       0xC0 /*End Collection */
};

firmwares/arduino-keyboard/makefile

Arduino UNOのR3だとmakefileを書き換える必要があるみたいです。

MCU = atmega8u2
MCU_AVRDUDE = at90usb82
MCU_DFU = at90usb82
#MCU = atmega16u2
#MCU_AVRDUDE = atmega16u2
#MCU_DFU = atmega16u2

#MCU = atmega8u2
#MCU_AVRDUDE = at90usb82
#MCU_DFU = at90usb82
MCU = atmega16u2
MCU_AVRDUDE = atmega16u2
MCU_DFU = atmega16u2

コンパイル&ファームウェア書き換え

ArduinoをDFUモードで繋いでおいてください

$ make
$ make dfu

サンプルを動かしてみる

接続して認識されるとスリープします。(Windowsで動作確認)

uint8_t buf[2] = { 0 };     /* Report buffer 8Byte */

void setup()
{
  Serial.begin(9600);
  delay(200);
}

void loop()
{
  sleep();
  while(1) {
    delay(1000);
  }
}

void sleep()
{
  buf[0] = 2;
  buf[1] = 1;
  Serial.write(buf,2);

  delay(200);

  // これを送らないとブルースクリーンになる
  buf[0] = 0;
  buf[1] = 0;
  Serial.write(buf,2);
}

最後に

レポートディスクリプタを正しく書いて、正しくデータを送ってあげれば用意されているファームウェアではできないこともできるようになるかな。 今回はスリープだけでしたが、拡張することで同じファームでキーボードの入力や、ボリューム調整キーの追加なども可能になると思います。

参考

サインスマート UNO 互換ボード for Arduino*USBケーブル付き*

サインスマート UNO 互換ボード for Arduino*USBケーブル付き*