🖱️

Pro Micro でスリープ防止装置を作る

3 min read

はじめに

世の中にあるほとんどのパソコンは放っておくとスリープするように設定されているはずです.これはたとえば離席時のセキュリティ保持のために重要です.しかし,人権のない自由に設定を変えられないパソコンで一時的にスリープを無効化したい場合があるかもしれません.そんなとき,手元に Pro Micro があればスリープを無効化するデバイスを簡単に作成できます.プログラミングには Arduino IDE を利用します.

1. 方法

作るもの

USBポートに接続しておくと,指定した時間(たとえば10秒)に1回マウスをほんのわずか動かすようなデバイスを作ります.これをUSBポートに接続しておくと,パソコンがスリープモードに入ることがありません.

考え方

通常であれば,マウスを少し動かす → 規定時間待機 のループでよいはずですが,せっかくなのでハードウェアボタンで ON/OFF する機能も付けてみます.普通に待機していると待機中は何もできず,したがってスイッチが押されているかどうかも監視できません.これを解決するために割り込みを使用します.以下のようにすると実現できますが,今回はタイマ割り込みを使用します[1]

  • ボタンが押されたことを検知して割り込みをかける.
  • マウスを動かす操作をタイマ割り込みで行う.ボタンの状態検知ループは常時回しておく.(←今回はこれ)

Arduino のタイマは簡単に使えるので直接書いてもよいのですが,その手間も惜しんで今回は MsTimer2 を使いました.これは,規定時間 (ms) 経ったら引数に与えた関数を実行します.JavaScript でいう SetInterval がただ1つグローバルに共有されているようなものです.

2. ハードウェア

マイコンボード(必須)

Pro Micro の 5V 16MHz 品を使用します.本家で買ってもよいですし, Amazon 等で互換品を安価に(といっても最近の半導体供給不足で値上がりしていますが)購入してもよいでしょう.

ON/OFF スイッチ(オプション)

はんだづけを省略し,繋いだら永遠にマウスが動き続けるだけでよい場合は 3. に進んでください.

ピン 2-4, 4-6 間にタクトスイッチ(押している間だけ導通するスイッチ)をはんだづけします.ピン 2,6 を内部でプルアップ(pinMode(2, INPUT_PULLUP))し,ピン 4 を GND に落としている(pinMode(4, OUTPUT); digitalWrite(4, LOW))ため,これでスイッチの押下状態を検出できます.以下の例では面実装タクトスイッチをはんだ吸い取り線の切れ端などを使って無理矢理ユニバーサル基板に固定しています.どうせたいした用途のデバイスではないので,動けばよかろうの精神でOKです.

内部を単純化して描くと以下のような回路になります.マイコンの内側で2ピンと6ピンが抵抗を介して+電源(VDD)に,4ピンが直接-電源(GND)に接続されています.スイッチが押されていない場合は2ピンの電圧は VDD に等しくなりますが,スイッチが押されると2ピンの電圧は GND と等しくなります.

3. ソースコード

このコードでは, 10,000 ms 後にタイマ割り込みでマウスを動かす → 50 ms 後にタイマ割り込みでマウスを逆に動かす,ということを行います.マウスの状態は isMouseJiggling という変数で管理します.

  • マウスを動かしたら割り込み周期を 50 ms に設定する.
  • マウスを戻し終わったら割り込み周期を 10,000 ms に設定する.

さらに,通常のループでスイッチの状態を監視し,スイッチが押されたらマウスを動かすかどうかの設定を切り替えます.ピン 2-4 間を導通させるとマウスが動くように, 4-6 間を導通させるとマウスを動かさないようにします.

状態の表示には(お行儀が悪いですが)オンボードの RXLED を流用します.

#include <MsTimer2.h>
#include <Mouse.h>
const int mouseMoveInterval = 10000; // マウスを動かす間隔
const int mouseMoveDelay = 50; // マウスを動かしてから戻すまでの間隔
const int mouseMoveAmount = 5; // マウス移動量
// 割り込みで使う変数なので volatile で宣言する
volatile bool isMouseJiggling = false; // マウスが移動中か?
volatile bool shouldDoJiggle = true; // マウス微動の初期状態.スイッチを実装しない場合は true にする

// スイッチを接続するピン
// 4ピンを LOW にするのは接続の便利のため.4ピンのかわりにGNDピンに繋いでもよい
const int lowPin = 4;
const int onPin = 2;
const int offPin = 6;

void jiggle() {
  if (!shouldDoJiggle) {
    return;
  }
  
  if (isMouseJiggling) {
    MsTimer2::set(mouseMoveInterval, jiggle); // Next: wait
    MsTimer2::start();
    Mouse.move(mouseMoveAmount,0,0);
  }
  else {
    MsTimer2::set(mouseMoveDelay, jiggle); // Next: move
    MsTimer2::start();
    Mouse.move(-mouseMoveAmount,0,0);
  }
  
  isMouseJiggling = !isMouseJiggling;
}

void setup() {
  Mouse.begin();
  pinMode(lowPin, OUTPUT);
  pinMode(onPin, INPUT_PULLUP);
  pinMode(offPin, INPUT_PULLUP);
  digitalWrite(lowPin, LOW);
  MsTimer2::set(100, jiggle);
  MsTimer2::start();
}

void loop() {
  // スイッチの状態監視
  if (digitalRead(onPin) == LOW) {
    shouldDoJiggle = false;
  }
  if (digitalRead(offPin) == LOW) {
    shouldDoJiggle = true;
  }
  // マウスを動かす場合 RXLED を点けておく
  if (shouldDoJiggle) {
    RXLED1;
  } else {
    RXLED0;
  }
}

更新履歴

2021-07-07 回路図と細かい解説を追加しました.

脚注
  1. ボタンで割り込みをかけてもよいのですが,チャタリングの扱いがやや面倒そうです ↩︎