class GroveAlphanumDisplay(object): def __init__(self, address=0x71, brightness=BRIGHT_DEFAULT, display_type=FOUR_TUBES): """ Constructor Args: address: I2C address, default is 0x71 brightness: Startup brightness, value between 0 and 15 display_type: Display type, can be one of 0: FOUR_TUBES and 1: TWO_TUBES """ self.address = address self.display_type = display_type self.font = display_font4 if display_type == FOUR_TUBES else display_font2 self.first_dot = False self.second_dot = False self.bus = Bus() self.data = [0] * 4 if self.display_type == FOUR_TUBES else 2 self.bus.write_i2c_block_data(self.address, REG_INIT, []) self.bus.write_i2c_block_data(self.address, DISP_ON, []) self.set_brightness(brightness) def clear(self): """ Clear display """ self.data = [0] * 4 if self.display_type == FOUR_TUBES else 2 self.first_dot = False self.second_dot = False self._show() def show(self, data): """ Show a string on the display Args: data: String to show. If it is longer than the display size (2 or 4), the string is trimmed to display length. """ if type(data) is str: self.data = [0] * 4 if self.display_type == FOUR_TUBES else 2 length = min(len(data), len(self.data)) for i in range(length): self.data[i] = self.font.get(data[i], 0) else: raise ValueError('Not support {}'.format(type(data))) self._show() def _show(self): """ Internal function to show the display data. First, create the I2C data to write to the display controller and then send it. """ wire_bytes = [0, 0] byte_10 = 0 byte_11 = 0 if self.display_type == FOUR_TUBES: for d in self.data: wire_bytes += [d & 0xFF, (d >> 8) & 0xFF] for i, d in enumerate(self.data): if i == 0: byte_10 |= (1 if (d & 0x02) else 0) << 4 byte_10 |= (1 if (d & 0x04) else 0) << 3 elif i == 1: byte_10 |= (1 if (d & 0x02) else 0) << 6 byte_11 |= (1 if (d & 0x04) else 0) << 6 elif i == 2: byte_10 |= (1 if (d & 0x02) else 0) << 5 byte_11 |= (1 if (d & 0x04) else 0) << 1 else: byte_11 |= (1 if (d & 0x02) else 0) << 2 byte_11 |= (1 if (d & 0x04) else 0) << 0 if self.first_dot: byte_10 |= self.font['first_dot'] & 0xFF byte_11 |= (self.font['first_dot'] >> 8) & 0xFF if self.second_dot: byte_10 |= self.font['second_dot'] & 0xFF byte_11 |= (self.font['second_dot'] >> 8) & 0xFF else: for i in [1, 0]: value = self.data[i] if i == 1 and self.first_dot: value |= self.font['dot'] if i == 0 and self.second_dot: value |= self.font['dot'] wire_bytes += [(value >> 8) & 0xFF, value & 0xFF] wire_bytes += [0] * 4 wire_bytes += [byte_10, byte_11] self.bus.write_i2c_block_data(self.address, 0, wire_bytes) def set_brightness(self, brightness): """ Sets the LED brightness. Args: brightness: Brightness as integer, value between 0 and 15 """ if brightness > BRIGHT_HIGHEST or brightness < 0: brightness = BRIGHT_HIGHEST self.bus.write_byte(self.address, REG_BRIGHT | brightness) def set_blink_type(self, blink_type): """ Configures the blinking of the display, can be one of: - 0: No blinking - 1: Blink with 2 Hz - 2: Blink with 1 Hz Args: blink_type: Blinking type """ if 0 <= blink_type <= 2: self.bus.write_byte(self.address, 0x81 | (blink_type << 1)) def set_dots(self, first, second): """ Sets the dots in the display. Args: first: If set, the first/upper dot is on second: If set, the second/lower dot is on """ self.first_dot = first self.second_dot = second self._show()
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) 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 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 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 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:] 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)
class DHT(object): DHT_TYPE = {'DHT11': '11', 'DHT22': '22', 'DHT10': '10'} DEFAULT_ADDR = 0x38 RESET_REG_ADDR = 0xba MAX_CNT = 320 PULSES_CNT = 41 def __init__(self, dht_type, pin=12, bus_num=1): if dht_type != self.DHT_TYPE['DHT11'] and dht_type != self.DHT_TYPE[ 'DHT22'] and dht_type != self.DHT_TYPE['DHT10']: print('ERROR: Please use 11|22|10 as dht type.') exit(1) self.dht_type = dht_type if dht_type == self.DHT_TYPE['DHT10']: self.bus = Bus(bus_num) self.addr = self.DEFAULT_ADDR self._dht10_init() else: self.pin = GPIO(pin, GPIO.OUT) self._last_temp = 0.0 self._last_humi = 0.0 @property def dht_type(self): return self._dht_type @dht_type.setter def dht_type(self, type): self._dht_type = type ######################## dht10 ############################ def _dht10_start_mess(self): reg_set = [0x33, 0x00] self.bus.write_i2c_block_data(self.addr, 0xac, reg_set) def _dht10_reset(self): self.bus.write_byte(self.addr, self.RESET_REG_ADDR) def _dht10_set_system_cfg(self): reg_set = [0x08, 0x00] self.bus.write_i2c_block_data(self.addr, 0xe1, reg_set) def _dht10_read_status(self): return self.bus.read_byte_data(self.addr, 0) def _dht10_init(self): time.sleep(.5) self._dht10_reset() # delay is needed after reset time.sleep(.3) self._dht10_set_system_cfg() status = self._dht10_read_status() # we must check the calibrate flag, bit[3] : 1 for calibrated ok,0 for Not calibrated. while status & 0x08 != 0x08: print("try calibrated again!n\n") self._dht10_reset() time.sleep(.5) self.bus.dth10_set_system_cfg() status = self._dht10_read_status() time.sleep(.5) ######################################################### def _read(self): if self.dht_type == self.DHT_TYPE['DHT10']: t = 0 h = 0 self._dht10_start_mess() time.sleep(.075) # we must check the device busy flag, bit[7] : 1 for busy ,0 for idle. while (self._dht10_read_status() & 0x80) != 0: time.sleep(.5) print("wait for device not busy") from smbus2 import SMBus, i2c_msg, SMBusWrapper with SMBusWrapper(1) as bus: msg = i2c_msg.read(self.addr, 6) data = bus.i2c_rdwr(msg) data = list(msg) t = (t | data[1]) << 8 t = (t | data[2]) << 8 t = (t | data[3]) >> 4 h = (h | data[3]) << 8 h = (h | data[4]) << 8 h = (h | data[5]) & 0xfffff t = t * 100.0 / 1024 / 1024 h = h * 200.0 / 1024 / 1024 - 50 return t, h # Send Falling signal to trigger sensor output data # Wait for 20ms to collect 42 bytes data else: self.pin.dir(GPIO.OUT) self.pin.write(1) time.sleep(.2) self.pin.write(0) time.sleep(.018) self.pin.dir(GPIO.IN) # a short delay needed for i in range(10): pass # pullup by host 20-40 us count = 0 while self.pin.read(): count += 1 if count > self.MAX_CNT: # print("pullup by host 20-40us failed") return None, "pullup by host 20-40us failed" pulse_cnt = [0] * (2 * self.PULSES_CNT) fix_crc = False for i in range(0, self.PULSES_CNT * 2, 2): while not self.pin.read(): pulse_cnt[i] += 1 if pulse_cnt[i] > self.MAX_CNT: # print("pulldown by DHT timeout %d" % i) return None, "pulldown by DHT timeout %d" % i while self.pin.read(): pulse_cnt[i + 1] += 1 if pulse_cnt[i + 1] > self.MAX_CNT: # print("pullup by DHT timeout %d" % (i + 1)) if i == (self.PULSES_CNT - 1) * 2: # fix_crc = True # break pass return None, "pullup by DHT timeout %d" % i total_cnt = 0 for i in range(2, 2 * self.PULSES_CNT, 2): total_cnt += pulse_cnt[i] # Low level ( 50 us) average counter average_cnt = total_cnt / (self.PULSES_CNT - 1) # print("low level average loop = %d" % average_cnt) data = '' for i in range(3, 2 * self.PULSES_CNT, 2): if pulse_cnt[i] > average_cnt: data += '1' else: data += '0' data0 = int(data[0:8], 2) data1 = int(data[8:16], 2) data2 = int(data[16:24], 2) data3 = int(data[24:32], 2) data4 = int(data[32:40], 2) if fix_crc and data4 != ((data0 + data1 + data2 + data3) & 0xFF): data4 = data4 ^ 0x01 data = data[0:self.PULSES_CNT - 2] + ('1' if data4 & 0x01 else '0') if data4 == ((data0 + data1 + data2 + data3) & 0xFF): if self._dht_type == self.DHT_TYPE['DHT11']: humi = int(data0) temp = int(data2) elif self._dht_type == self.DHT_TYPE['DHT22']: humi = float(int(data[0:16], 2) * 0.1) temp = float( int(data[17:32], 2) * 0.2 * (0.5 - int(data[16], 2))) else: # print("checksum error!") return None, "checksum error!" return humi, temp def read(self, retries=15): for i in range(retries): humi, temp = self._read() if not humi is None: break if humi is None: return self._last_humi, self._last_temp self._last_humi, self._last_temp = humi, temp return humi, temp
class ButtonTypedI2c(Button): def __init__(self, address=0x03): self.bus = Bus() self.addr = address self.dev_id = 0 self.val = 0 self.probeDevID() self.get_val() self.send_Byte(0x02) app1 = self.status_read() while 1: self.status_read() time.sleep(1.0) continue def set_devID(self, reg, size): self.bus.write_byte_data(self.addr, reg, size) def send_Byte(self, reg): self.bus.write_byte(self.addr, reg) def get_devID(self, data): return self.bus.read_byte(self.addr, data) def get_data(self, reg, len): return self.bus.read_i2c_block_data(self.addr, reg, len) def probeDevID(self): for i in range(4): id = self.get_data(0x00, 4) did = 0 for j in range(4): did = (did >> 8) | (int(id[j]) << 24) #print("DEV_ID = {:8X}".format(did)) if (did >> 16) == 0x2886: self.dev_id = did return self.dev_id self.get_devID(True) def get_val(self): if (self.dev_id & 0xFFFF) == 0x0002: self.val = 5 print("Grove 5_way tactile Switch Insert") self.key_names = grove_5way_tactile_keys return self.val elif (self.dev_id & 0xFFFF) == 0x0003: self.val = 6 print("Grove 6_pos dip Switch Insert") self.key_names = grove_6pos_dip_switch_keys return self.val def status_read(self): app = self.get_data(0x01, 4 + self.val) #print("get event ={}".format(app)) for i in range(0, self.val): print("{} : RAW- ".format(self.key_names[i]), end='') print("{} ".format(app[i + 4] & 1 and "HIGH" or "LOW")) if (self.dev_id & 0xFFFF) == 0x0002: print("{} ".format(app[i + 4] & 1 and "RELEASEND" or "PRESSED")) elif (self.dev_id & 0xFFFF) == 0x0003: print("{} ".format(app[i + 4] & 1 and "OFF" or "ON")) for i in range(0, self.val): if app[i + 4] & ~1: print("{} ".format(self.key_names[i])) print(": EVENT - ") if app[i + 4] & (1 << 1): print("SINGLE-CLICK") if app[i + 4] & (1 << 2): print("DOUBLE-CLICL") if app[i + 4] & (1 << 3): print("LONG-PRESS") if app[i + 4] & (1 << 4): print("LEVEL-CHANGED") print("") return app