Source code for grove.button.button_i2c
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Grove Base Hat for the Raspberry Pi, used to connect grove sensors.
# Copyright (C) 2018 Seeed Technology Co.,Ltd.
#
'''
'''
from __future__ import print_function
from grove.i2c import Bus
from grove.button import Button
import threading
import time
__all__ = ["ButtonTypedI2c", "NAME_5_WAY_SWITCH", "NAME_6_POS_DIP_SWITCH"]
NAME_5_WAY_SWITCH = "Grove-5-Way-Switch"
""" The Button name to compare with return value of :class:`ButtonTypedI2c.name` """
NAME_6_POS_DIP_SWITCH = "Grove-6-Pos-DIP-Switch"
""" The Button name to compare with return value of :class:`ButtonTypedI2c.name` """
_grove_5way_tactile_keys = ("KEY A","KEY B","KEY C","KEY D","KEY E")
_grove_6pos_dip_switch_keys = ("POS 1","POS 2","POS 3","POS 4","POS 5","POS 6")
_CMD_GET_DEV_ID = 0x00
_CMD_GET_DEV_EVENT = 0x01
_CMD_EVENT_DET_MODE = 0x02
_CMD_BLOCK_DET_MODE = 0x03
_CMD_TEST_GET_VER = 0xE2
VID_MULTI_SWITCH = 0x2886
PID_5_WAY_TACTILE_SWITCH = 0x0002
PID_6_POS_DIP_SWITCH = 0x0003
_CYCLE_PERIOD = 0.08 # 80 ms
[docs]
class ButtonTypedI2c(Button):
'''
I2C Button/Switch Array Class
provide event checking ability to derived class,
should not use directly by end-user.
The checking events include:
- Button.EV_SINGLE_CLICK
- Button.EV_DOUBLE_CLICK
- Button.EV_LONG_PRESS
- Button.EV_LEVEL_CHANGED
Args:
address(int): optional, the I2C address of the connected device.
evt_en(bool): optional, default True
True: provide event checking ability.
False: used in poll environment, manually call :class:`ButtonTypedI2c.read`.
'''
def __init__(self, address = 0x03, evt_en = True):
super(ButtonTypedI2c, self).__init__(0)
self.bus = Bus()
self._addr = address
# Initialise the I2C button device
self.dev_id = 0
self._probe_uid()
self._version = 0
self.version()
self._size = self.size()
self._set_mode(True)
self.__last_evt = None
self.__last_evt = self.read()
self.key_names = _grove_5way_tactile_keys
if self._size == 6:
self.key_names = _grove_6pos_dip_switch_keys
self.__thrd_exit = False
self.__thrd = None
if not evt_en:
return
if self.__thrd is None or not self.__thrd.is_alive():
self.__thrd = threading.Thread( \
target = ButtonTypedI2c.__thrd_chk_evt, \
args = (self,))
self.__thrd.setDaemon(True)
self.__thrd.start()
def __del__(self):
if not self.__thrd:
return
self.__thrd_exit = True
while self.__thrd.isAlive():
time.sleep(_CYCLE_PERIOD / _CYCLE_UNIT)
self.__thrd.join()
# Thread to check events
def __thrd_chk_evt(self):
self.__last_time = time.time();
while not self.__thrd_exit:
# or self.__state != self.KEY_STATE_IDLE:
t = time.time()
dt, self.__last_time = t - self.__last_time, t
evt = self.read()
if not evt[0]:
time.sleep(_CYCLE_PERIOD)
continue
for i in range(0, self.size()):
if evt[i + 1] & ~self.EV_RAW_STATUS:
pressed = bool(evt[i + 1] & self.EV_RAW_STATUS)
self._index = i
self._send_event(evt[i + 1], pressed, t)
time.sleep(_CYCLE_PERIOD)
def _probe_uid(self):
ID_LEN = 4
for tr in range(4):
v = self.bus.read_i2c_block_data(self._addr, _CMD_GET_DEV_ID, ID_LEN)
# print("GET_DEV_ID = {}".format(v))
did = 0
for i in range(ID_LEN):
did = (did >> 8) | (int(v[i]) << 24)
# print("DEV_ID = {:8X}".format(did))
if (did >> 16) == VID_MULTI_SWITCH:
self.dev_id = did
return self.dev_id
self.bus.read_byte(self._addr, True)
[docs]
def version(self):
'''
Get the device firmware version.
Returns:
(int): firmware version, the first version is 1
'''
VER_LEN = 10
if not self.dev_id:
return 0
v = self.bus.read_i2c_block_data(self._addr, _CMD_TEST_GET_VER, VER_LEN)
# print("GET_VER = {}".format(str(v)))
version = v[6] - ord('0')
version = version * 10 + (v[8] - ord('0'))
# print("version = {}".format(version))
self._version = version
return self._version
[docs]
def size(self):
'''
Get the button count the device have.
Returns:
(int): button count
'''
if (self.dev_id >> 16) != VID_MULTI_SWITCH:
return 0
if (self.dev_id & 0xFFFF) == PID_5_WAY_TACTILE_SWITCH:
return 5
if (self.dev_id & 0xFFFF) == PID_6_POS_DIP_SWITCH:
return 6
return 0
[docs]
def name(self, index = None):
'''
Get the device name or specified button name
Args:
index(int): optional, the index number of button to get name.
if not specified, return the device name.
Returns:
(string): the name of the device or pecified button
'''
if (self.dev_id >> 16) != VID_MULTI_SWITCH:
return "Invalid dev"
if not index is None:
if index < 0 or index >= self._size:
return "Invalid index"
return self.key_names[index]
if (self.dev_id & 0xFFFF) == PID_5_WAY_TACTILE_SWITCH:
return NAME_5_WAY_SWITCH
if (self.dev_id & 0xFFFF) == PID_6_POS_DIP_SWITCH:
return NAME_6_POS_DIP_SWITCH
return "Invalid dev"
def _set_mode(self, enable):
if not self.dev_id:
return None
v = _CMD_BLOCK_DET_MODE
if enable:
v = _CMD_EVENT_DET_MODE
self.bus.write_byte(self._addr, v)
return True
[docs]
def read(self):
'''
Get the button array status
Returns:
(list): a list has the size button count + 1
item [0] indicate if there is a event (bit 0x80).
bit 0x80 set if one or more the switches have event.
bit 0x80 clear if no one has event.
item [ 1 + `index` ] indicate the event of button specified
by index, be bits combination of
- Button.EV_LEVEL_CHANGED
- Button.EV_SINGLE_CLICK
- Button.EV_DOUBLE_CLICK
- Button.EV_LONG_PRESS
'''
EVT_LEN = 4
if not self.dev_id:
return None
size = EVT_LEN + self._size
v = self.bus.read_i2c_block_data(self._addr, _CMD_GET_DEV_EVENT, size)
if self._version > 1 or self.__last_evt is None:
return v[EVT_LEN - 1:]
# Fix: v0.1 will miss event BTN_EV_LEVEL_CHANGED
# if this API called frequently.
for i in range(self._size):
if (v[EVT_LEN + i] ^ self.__last_evt[1 + i]) & self.EV_RAW_STATUS:
v[EVT_LEN + i] |= Button.EV_LEVEL_CHANGED
v[EVT_LEN - 1] |= 0x80
self.__last_evt = v[EVT_LEN - 1:]
return v[EVT_LEN - 1:]
[docs]
def is_pressed(self, index = 0):
'''
Get the button status if it's being pressed ?
:class:`ButtonTypedI2c.read` must be called before this api call
when used with poll method object (created with evt_en = False).
Args:
index(int): optional, the index number of button to be checked.
must be specified for this device.
Returns:
(bool):
True if the button is being pressed.
False if not.
'''
return not bool(self.__last_evt[index + 1] & self.EV_RAW_STATUS)
def main():
switch = ButtonTypedI2c(evt_en = False)
print("{} v{} Inserted".format(switch.name(), switch.version()))
print("A {} Button/Switch Device Ready".format(switch.size()))
while True:
evt = switch.read()
print("EVENT = {}".format(evt))
for i in range(0, switch.size()):
print("{}: ".format(switch.name(i)), end='')
print("{} ".format(switch.is_pressed(i) and "LOW " or "HIGH"), end='')
if switch.name() == NAME_5_WAY_SWITCH:
print("{} ".format(evt[i + 1] & switch.EV_RAW_STATUS and "RELEASED" or "PRESSED "), end='')
elif switch.name() == NAME_6_POS_DIP_SWITCH:
print("{} ".format(evt[i + 1] & switch.EV_RAW_STATUS and "OFF" or "ON "), end='')
print(" ", end='')
print("S" if evt[i + 1] & Button.EV_SINGLE_CLICK else " ", end='')
print("D" if evt[i + 1] & Button.EV_DOUBLE_CLICK else " ", end='')
print("L" if evt[i + 1] & Button.EV_LONG_PRESS else " ", end='')
print("C" if evt[i + 1] & Button.EV_LEVEL_CHANGED else " ", end='')
print(" ", end='')
print()
time.sleep(1.0)
if __name__ == "__main__":
main()