microbit
micropython

micro:bit でボタンの判定(単押、連打、押しっぱなし、同時押)

概要

micro:bitのボタンで以下の判定する方法を考えてみました。

  • ボタン1回押す
  • ボタン連打
  • ボタン押しっぱなし
  • AボタンをBボタンを同時に押す

背景

micro:bitにはAボタン、Bボタンがあります。
MicroPython」を使用したボタンの制御で意外と時間使ったので、備忘録として作成。

最初、適当に作ったプログラムでの状況は以下

操作 想定している動き 実際の動き
ボタン1回押す、ボタン連打 ボタンを押した回数だけ処理Xを実行 ボタンの押下数より処理の実行回数が少ない
ボタン押しっぱなし ボタンを押している間は処理Xを高速で繰り返し実行 1回しか処理Xが実行されない。または、処理がまったく実行されない
Aボタン、Bボタンを同時に押す 処理Yを実行 処理Xが実行された後、処理Yが実行される

検証

micro:bit用MicroPythonのリファレンスマニュアルにはボタンクラスのメソッドについて以下3つの説明があります。

is_pressed()
指定のボタンが押されていれば True を返し、押されていなければ False を返します。
was_pressed()
デバイスが起動されてから、もしくは前回このメソッドが呼ばれてからボタンが(上から下へ)
押されたかによって True または False を返します。このメソットを呼び出すとボタンが
押されたかの状態がクリアされるので、再度このメソッドが True を返すようにするには、
このメソッドを呼び出す前にボタンが押されなければなりません。
get_presses()
ボタンを押した回数の合計を返し、返す前に回数をゼロにリセットします。

3メソッドの動きを確認するために以下コードを作成

from microbit import *

#---------------------------
# Divid screen
#---------------------------

# left top
div_1_x = 0
div_1_y = 0

# left bottom
div_2_x = 0
div_2_y = 3

# right top
div_3_x = 3
div_3_y = 0

# right bottom
div_4_x = 3
div_4_y = 3

while True:

  #---------------------------
  # I want to detect under operation.
  # Which check is best?
  # 1) Click
  # 2) Press
  # 3) Click button repeatedly
  # 4) Press A + B 
  #---------------------------

  base_x = 0
  base_y = 0

  #---------------------------
  # 1. "is_pressed()"
  #---------------------------
  base_x = div_1_x
  base_y = div_1_y

  if button_a.is_pressed() == True:
    display.set_pixel(base_x, base_y,   7)
    display.set_pixel(base_x, base_y+1, 0)
  else:
    display.set_pixel(base_x, base_y,   0)
    display.set_pixel(base_x, base_y+1, 7)

  #---------------------------
  # 2. "was_pressed()"
  #---------------------------
  base_x = div_2_x
  base_y = div_2_y

  if button_a.was_pressed() == True:
    display.set_pixel(base_x, base_y,   7)
    display.set_pixel(base_x, base_y+1, 0)
  else:
    display.set_pixel(base_x, base_y,   0)
    display.set_pixel(base_x, base_y+1, 7)

  #---------------------------
  # 3. "get_presses()"
  #---------------------------
  base_x = div_3_x
  base_y = div_3_y

  press_count = button_a.get_presses()

  if press_count > 0:
    display.set_pixel(base_x,   base_y,   7)
    display.set_pixel(base_x+1, base_y,   press_count)
    display.set_pixel(base_x,   base_y+1, 0)
  else:
    display.set_pixel(base_x,   base_y,   0)
    display.set_pixel(base_x+1, base_y,   0)
    display.set_pixel(base_x,   base_y+1, 7)

  #---------------------------
  # 4. "is_pressed" (A + B)
  #---------------------------
  base_x = div_4_x
  base_y = div_4_y

  if button_a.is_pressed() == True and button_b.is_pressed() == True:
    display.set_pixel(base_x, base_y,   7)
    display.set_pixel(base_x, base_y+1, 0)
  else:
    display.set_pixel(base_x, base_y,   0)
    display.set_pixel(base_x, base_y+1, 7)

  sleep(500)

検証結果

リファレンス通りですが、実際に動かし以下の通り確認しました。

is_pressed()
押しっぱなしでは、ずっとTrueの状態。1回押しや、連打はタイミングによって、
Trueを返さない場合があり。ABボタン同時押はタイミングがずれても検知できた。
was_pressed()
押しっぱなしでは最初にTrueを返したが、2回目のループ以降はFalseになる。
1回押し、連打では押したループ間に1回でも押されていればTrueを返す。
ABボタン同時押はタイミングがずれると、例えばボタンA側がTrue、B側がFalseになり、
検知できない場合があり。
is_presses()
押しっぱなしでは最初にTrueを返したが、2回目のループ以降はFalseになる。
1回押し、連打ではループ間隔ごとに押した回数を取得することがでる。
ABボタン同時押はタイミングがずれると、例えばボタンA側がTrue、B側がFalseになり、
検知できない場合があり。

結論

今回、以下の判断基準で処理するのがだいたい良いかなとなりました。

  • 1回押し、連打判定
    0.01秒ごとにボタンの状態をチェックして、ボタンが離されたときに処理Xを実行する。

  • 押しっぱなし判定
    0.5秒間、押下状態が続いたら処理Xを実行する。
    ちなみに、0.5秒以内に離されれば1回押しとなる。
    逆に、もう片方のボタンも押されれば同時押しとなる。

  • AB同時押し判定
    片方のボタンを押している状態でもう片方のボタンを押せば同時押しとして処理Yを実行。
    なお、押しっぱなし状態で、もう片方のボタンを押しても同時押しになります。
    (押しっぱなし状態から同時押しへの移行は要件によって変わりそう)

サンプルソース

from microbit import *

# ボタンが押されている間、最大50までカウントアップ。
button_a_is_purresing = 0
button_b_is_purresing = 0

while True:

  if button_a.is_pressed() and button_b.is_pressed():

    ## ボタンAB同時押と判定 ##
    ## ここで処理Yを実行 ##

    # 変数とイベントをクリア
    button_a_is_purresing = 0
    button_b_is_purresing = 0
    button_a.was_pressed()
    button_b.was_pressed()
    sleep(1000)
  else:

    # -------------------
    # ボタンA
    # -------------------
    if button_a.was_pressed() == True:
      # 1回押し判定
      button_a_is_purresing += 1
      sleep(10)
      continue
    elif button_a.is_pressed() == True:
      # 押しっぱなし判定
      if button_a_is_purresing <= 50:
        # ABボタン同時押しや、
        # 押しっぱなしの可能性があるので0.5秒待つ
        button_a_is_purresing += 1
        sleep(10)
        continue

      ## 押しっぱなしと判定 ##
      ## ここで処理Xを実行 ##

    elif button_a_is_purresing > 0:
      ## 1回押し、連続押しと判定 ##
      ## ここで処理Xを実行 ##
      button_a_is_purresing = 0

    # -------------------
    # ボタンB
    # -------------------

    # 必要な場合、
    # ここにボタンAと同様に処理を追加

リンク

使用例 : DispNumSoroban.py
https://github.com/soramaru777/DispNumSoroban/blob/master/DispNumSoroban1.py

micro:bit - 日本公式サイト
http://microbit.org/ja/

MicroPython - micro:bit
http://microbit-micropython.readthedocs.io/ja/latest/