お疲れ様キーボードを作る!

動機

以前Twitterでこんな投稿を見つけました。

「お疲れ様でした」や「ありがとうございます」等の定型文が入力できるキーボードです。
めっちゃ便利そうなので自分で作ってみることにしました!

仕様

とりあえずの構想はこんな感じで行きます

  • キーを押したことをArduino Nanoで認識
  • Arduino NanoからUSB経由でパソコンにデータを送信
  • パソコン側でPythonプログラムがデータを受信
  • プログラムが文字を打ち込む

必要なものと環境

  • 300円デバイス、Arduino Nano
  • ボタンスイッチ
  • Python3.5(3.6以上だと動作しない可能性あり?)
  • Windows 10(ここは何でも構いません)

ボタンとArduino

まず、ボタンを押したことをArduinoで検知する部分を作ります。押すと導通するスイッチとArduinoをはんだ付けでつなぎます。

Arduinoとスイッチ4つを配線

スイッチを押すとD2~D4が0Vに落ちます。

基盤にはんだ付け

Arduino側プログラム

スイッチの0V落ちを検知して、パソコンにデータを送るプログラムを作成します。

const int pin1 = 2;
const int pin2 = 3;
const int pin3 = 4;
const int pin4 = 5;

void setup() {
  Serial.begin(9600);
  pinMode(pin1,INPUT_PULLUP);
  pinMode(pin2,INPUT_PULLUP);
  pinMode(pin3,INPUT_PULLUP);
  pinMode(pin4,INPUT_PULLUP);
}

void loop() {
  int x1,x2,x3,x4;
  x1 = digitalRead(pin1);
  x2 = digitalRead(pin2);
  x3 = digitalRead(pin3);
  x4 = digitalRead(pin4);
  
  if(x1==0) Serial.println(1);
  if(x2==0) Serial.println(2);
  if(x3==0) Serial.println(3);
  if(x4==0) Serial.println(4);
  if(x1*x2*x3*x4!=0) Serial.println(0);
  
  delay(10);
}

スイッチ1~4が押されていれば(電圧が0Vならば)その数字を、すべて押されていなければ(x1~x4がすべて5Vならば)0を送信します。
スイッチが押されていない時(端子に何も接続されていない時)の電圧は、通常は不安定にならないように5Vまたは0Vに接続しなければいけませんがArduinoでは内部的にプルアップまたはプルダウンを選べるようです。

pinMode(pin1,INPUT_PULLUP);

このプログラムだとボタンを押している間ものすごい速さでデータが送信され続けます。一見無駄なようですが、データを受ける側のタイミングが悪いと上手に受け取れなかったりするので、受ける側で対処することにします。

パソコン側Pythonプログラム

最後に、Arduinoからデータを受け取って文字を打ち込むプログラムを作ります。シリアル通信が簡単に書けるPythonを使っていきます。

#coding utf-8
#シリアル通信で受け取ったデータによって
#定型文をエディタにペーストする
import serial
import time
import pyautogui as pa
import pyperclip

def put_text(t):
	tmp = pyperclip.paste()	#今のクリップボードのデータを一時保存しておく
	pyperclip.copy(t)	#テキストをクリップボードに読み込む
	pa.hotkey("ctrl","v")	#”Ctrl+V”を入力
	pyperclip.copy(tmp)	#保存しておいたデータをクリップボードに戻す
	return

def main():
	#通信ポートおよびボーレートの設定
	ser = serial.Serial('COM4',9600)

	while True:
		#データを一行受信
		x = ser.readline()

		#受け取ったデータによって処理を実行
		if (x==b'1\r\n'):
			put_text("お疲れ様です")
		if (x==b'2\r\n'):
			put_text("ありがとうございます")

		#0が送られてくるまで受信のみ
		while (x!=b'0\r\n'):
			x = ser.readline()
	ser.close()

if __name__=="__main__":
	main()

通信ポートの設定の部分はArduinoが接続しているポートに合わせて変えて下さい。(windowsなら「COM◯」、Linuxなら「/dev/ttyUSB0」等)。
pyautoguiやpyperclipなどのライブラリも適宜pipで入れて下さい。僕はpyautoguiがインストール出来ずに割と悩みましたがPython3.5を使うことで解決しました。この辺の原因は謎です。

pip install pyserial
pip install pyautogui
pip install pyperclip

 

エラー処理も何もしていないプログラムなので、参考程度に使用して下さい。

テストしてみる

ArduinoKeyboard.pyを実行しておきます。その状態でスイッチを押すと…

ちゃんと打ててる!

出来ました!

2017/12/26追記:

別の環境で動かそうとしてみたところ、serialパッケージが無いと言われる。pipでインストールしたけど…

pip install serial

エラーが…
serialじゃなくてpyserialらしい

pip uninstall serial
pip install pyserial

これで動いた

 

 

トイレの自動洗浄を自作で後付けしてみた②(プログラム編)

前回自宅トイレのレバーをひねる動きを自動化すべく、貯水タンクの栓を引っ張る機構とセンサの設置を行いました。今回は用が済んだら流れてくれるようにArduinoにプログラムを書き込みたいと思います。

基本動作

実現したい動作は大体こういう感じです

  • 便器から離れたら水を流す
  • 立った時も座った時も反応する
  • 間違えて流さない
  • 離れたらすぐに反応してほしい

この条件を満たすようにプログラムを考えます。とりあえずの構想は、

  1. センサで測った距離が80cm以上の時は何もしない
  2. 距離が80cm以下で6秒間センサ値が変化しなかったら、座っているまたは立っていると判断し流しスタンバイ状態
  3. 距離が大きく離れたらモータを動かして流す
  4. 5秒くらいしたら止める
  5. 繰り返し

こんな感じで行こうと思います。

実際のコード

実際にAruduinoに書いた制御プログラムです。

#include <Servo.h>

const int servoPin = 3;    // サーボの接続ピン
const int ledPin = 8;     //表示用LEDの接続ピン
const int sensorPin = 0;  //距離センサの接続ピン
Servo myservo;             // サーボオブジェクトを生成
int pos = 0;               // サーボのポジション(変数)

void setup()
{
  myservo.attach(servoPin);  // サーボ変数をピンに割り当て
  pinMode(ledPin, OUTPUT);
}

//map関数をdoublueで使えるように
double dmap(double value, double fromMin, double fromMax, double toMin, double toMax) {
  double a = (toMax - toMin) / (fromMax - fromMin);
  double b = toMax - a * fromMax;
  return a * value + b;
}

//センサーで距離を計測
double measure_dist() {
  double data = analogRead(sensorPin);  //センサーからの値(0~1023の整数)を取得
  double v = dmap(data, 0, 1023, 0.0, 5.0);   //0~1023→0V~5Vに変換
  double dist_inv = dmap(v, 0.4, 3.1, 0.015, 0.15);   //0V~5V→距離の逆数に変換
  return 1.0 / dist_inv;    //距離を返す
}

void loop()
{
  double dist = 0;
  double dist_old = 0;
  int i = 0;
  while (i < 5) {
    dist = measure_dist();
    
    //距離が変化していなかったらカウントを増やす
    //変化していたらカウントを0にする
    if (abs(dist - dist_old) < 5 and dist < 40) {
      i++;
    } else {
      i = 0;
    }

    dist_old = dist;
    delay(1000);
  }

  digitalWrite(ledPin, 1);

  //最大10分経ったらリセットする
  int sec = 0;
  while (sec < 60 * 10) {
    dist = measure_dist();

    //距離が離れたら水を流す
    if ( dist - dist_old > 10) {
      digitalWrite(ledPin, 0);
      myservo.write(180);   //180[deg]にする
      delay(6000);    //6秒待つ
      myservo.write(0);   //0[deg]にする
      break;
    }
    
    delay(1000);
    sec++;
  }
  
}

 

トイレの自動洗浄を自作で後付けしてみた①(ハードウェア編)

動機

サービスエリアとかのトイレで、立ち上がるとセンサーで感知して自動で流してくれるやつが割と便利なので自宅にも欲しいと思ったのが動機。駆動部品にはサーボモータが使えそう。人の動き検知には超音波距離センサが使えそう。
あと安かったから3つも買ってしまったArduino nanoが使い道がなくて持て余してたからです。

基本構想

最初はサーボモータでレバー捻ればいいんでしょと思ったけれど、レバーに直接モータをつなぐだけではトルク不足で動きませんでしたorz
家族も使うものなので、ここは元の動きをそのまま残しつつ機能を追加することを目標に設計が必要そうです。

取り敢えず貯水タンクを開けてみる↓

ゴチャゴチャしている…(・_・;)
ちょっと考えて機構はこんな感じと判明しました。

機構の簡単な図解

貯水タンクを横から見た図です。レバーを手前or奥にひねるとチェーンで繋がった小のふたor大のふたが開くようになっています。

他に水位が一定に戻るまで水を貯めるためのウキが付いていますが、今回は関係ないので省きます。

というわけで流すにはフタをモータで引っ張れば良さそうです。元の動きを阻害しない点、取り付けやすい点を考えて、こんな感じにしました。

モータ取り付け後の図↓

使用する部品

Arduinoは最安のnanoで大丈夫です。モータはSG90が有名ですがちょっとパワーが足りないかも…。ギリギリ行けますが高トルク版のSG92Rだと安心です。距離センサはシャープ製の赤外線センサを使いました。(ちょっと高いので距離が測れる他のセンサで代用するのもあり)

モータの取付け

モータはタンクに固定しなければならないのですが、横側に何故かいい感じの穴が空いていたので(サイズもぴったり!?)そこにはめました。穴がない方はガムテープで固定するなり接着剤とかで固定するなりすればいいと思います。要は紐が引っ張れればどこに付けてもいいです。僕が使ったサーボモータSG90では引っ張るトルクが結構ギリギリで、回転半径を決めるのに苦労しました(といっても糸を通す穴を変えるだけですが)。あまり小さくしてしまうと引っ張る長さが足りなくて流れなかったり、トルクが足りないとモータが動かなかったりします。あとArduino nanoだと出力電流が足りなくて、別で電源を取る形になっています。Arduino UNOで試したら力が倍くらいになりました。

0[deg]→180[deg]まで目一杯回してフタの開閉を行うことが出来ました!

センサの取付け

座ったり立ったりするのを検知するセンサはこの赤外線距離センサを使います。

シャープ測距モジュール GP2Y0A21YK
http://akizukidenshi.com/catalog/g/gI-02551/

このセンサは数cm〜80cmの距離に応じて電圧を出力してくれるもので、5V動作なのでArduinoやラズパイにそのまま繋ぐだけの有能センサ(ただし、精度はお察し)です。こいつを便器の下辺りに取り付けておきます。

センサの横に赤色LEDをつけてます。↓

回路づくり

モータとセンサとArduinoNanoを繋げるために基盤を作りました。
モータとセンサの電源は、Arduinoの5V出力から取っている記事が多いですが、それだとモータを回すのに十分な電力が取れませんでしたorz。

代わりにUSBから5V電源を取ることにしました。(ArduinoはUSBを電源にする代わりに安定した5V電源をVin端子とGND端子に繋ぐことで動かすことが出来るみたいです。)

USB type-Bが刺せるアダプタ。amazonで10個で150円くらい!↓

回路図を書くとこんな感じです。(まだ回路図を書くのに慣れていないので、見づらいですがご容赦を)

回路図↓

実際の基盤。10円玉4個くらいのサイズ↓

ハードウェア構成は取り敢えずこんな感じです。次回は制御プログラムの方を作っていきます。