Module pytell
[hide private]
[frames] | no frames]

Source Code for Module pytell

  1  # pytell.py 
  2   
  3  ''' 
  4  Class that represents a 4 digit 7-segment display PyTell from Didel (http://www.didel.com) 
  5   
  6   This software is part of the raspibrick module. 
  7   It is Open Source Free Software, so you may 
  8   - run the code for any purpose 
  9   - study how the code works and adapt it to your needs 
 10   - integrate all or parts of the code in your own programs 
 11   - redistribute copies of the code777 
 12   - improve the code and release your improvements to the public 
 13   However the use of the code is entirely your responsibility. 
 14   ''' 
 15   
 16  from smbus import * 
 17  import RPi.GPIO as GPIO 
 18  from threading import Thread 
 19  import time 
20 21 -class SMBusException(Exception): pass
22
23 24 # ------------------- class PyTell ---------------------- 25 -class PyTell():
26 ''' 27 Abstraction of the 4 digit 7-segment display PyTell from Didel (http://www.didel.com) attached to the I2C port. 28 If no display is found, all display methods return immediately. The static variable SharedConstants.PATTERN defines a dictionary 29 that maps ASCII characters to display patterns and can be modified by the user program. 30 31 Default: 32 33 The 7 segments have the following binary weights 34 1 35 - 36 32 | |2 37 38 64 39 - 40 16 | |4 41 - 42 8 43 44 The decimal points has weight 128. 45 46 ''' 47 48 DEBUG = False 49 VERSION = "1.01 - April 2016" 50 51 ''' 52 Character to binary value mapping for 4 digit 7 segment display 53 ''' 54 PATTERN = {' ': 0, '!': 134, '"': 34, '#': 0, '$': 0, '%': 0, '&': 0, '\'': 2, '(': 0, ')': 0, 55 '*': 0, '+': 0, ',': 4, '-': 64, '.': 128, '/': 82, '0': 63, '1': 6, '2': 91, '3': 79, 56 '4': 102, '5': 109, '6': 125, '7': 7, '8': 127, '9': 111, ':': 0, ';': 0, '<': 0, 57 '=': 72, '>': 0, '?': 0, '@': 93, 'A': 119, 'B': 124, 'C': 57, 'D': 94, 'E': 121, 58 'F': 113, 'G': 61, 'H': 118, 'I': 48, 'J': 14, 'K': 112, 'L': 56, 'M': 85, 'N': 84, 59 'O': 63, 'P': 115, 'Q': 103, 'R': 80, 'S': 45, 'T': 120, 'U': 62, 'V': 54, 'W': 106, 60 'X': 73, 'Y': 110, 'Z': 27, '[': 57, '\\': 100, ']': 15, '^': 35, '_': 8, '`': 32, 61 'a': 119, 'b': 124, 'c': 88, 'd': 94, 'e': 121, 'f': 113, 'g': 61, 'h': 116, 'i': 16, 62 'j': 12, 'k': 112, 'l': 48, 'm': 85, 'n': 84, 'o': 92, 'p': 115, 'q': 103, 'r': 80, 's': 45, 63 't': 120, 'u': 28, 'v': 54, 'w': 106, 'x': 73, 'y': 110, 'z': 27, '{': 0, '|': 48, '}': 0, '~': 65} 64 65 # ----------------------- static methods --------------------- 66 @staticmethod
67 - def debug(msg):
68 if PyTell.DEBUG: 69 print "PyTell debug->", msg
70 71 @staticmethod
72 - def getVersion():
73 return PyTell.VERSION
74 75 @staticmethod
77 ''' 78 Returns a string with all displayable characters taken from PATTERN dictionary. 79 @return: The character set that can be displayed 80 ''' 81 s = "<SPACE>" 82 k = 33 83 while k < 127: 84 ch = chr(k) 85 if PyTell.PATTERN[ch] != 0: 86 s = s + ch 87 k += 1 88 return s
89 90 @staticmethod
91 - def toHex(intValue):
92 ''' 93 Returns a string with hex digits from given number (>0, any size). 94 @param number: the number to convert (must be positive) 95 @return: string of hex digits (uppercase), e.g. 0xFE 96 ''' 97 return '%02x' % intValue
98 99 @staticmethod
100 - def toBytes(intValue):
101 ''' 102 Returns a list of four byte values [byte#24-#31, byte#16-#23, byte#8-#15, byte#0-#7] of given integer. 103 @param number: an integer 104 @return: list with integers of 4 bytes [MSB,..., LSB] 105 ''' 106 byte0 = intValue & 0xff 107 byte1 = (intValue >> 8) & 0xff 108 byte2 = (intValue >> 16) & 0xff 109 byte3 = (intValue >> 24) & 0xff 110 return [byte3, byte2, byte1, byte0]
111 112 @staticmethod
113 - def toInt(hexValue):
114 ''' 115 Returns an integer from given hex string 116 @param number: a string with the number to convert, e.g. "FE" or "fe" or "0xFE" or "0XFE" 117 @return: integer number 118 ''' 119 return int(hexValue, 16)
120 121 @staticmethod
122 - def delay(timeout):
123 time.sleep(timeout / 1000.0)
124 125 # ----------------------- Constructor ------------------------
126 - def __init__(self, i2c_address = 0x20):
127 ''' 128 Creates a display instance with display set to given i2c address (default: 0x20). 129 Then the display is cleared. 130 @param i2c_address: the i2c address (default: 0x20) 131 ''' 132 self.i2c_address = i2c_address 133 self._bus = None 134 self._isReady = True 135 self._tickerThread = None 136 self._blinkerThread = None 137 138 try: 139 if GPIO.RPI_REVISION > 1: 140 self._bus = SMBus(1) # For revision 2 Raspberry Pi 141 PyTell.debug("I2C at bus 1 detected") 142 else: 143 self._bus = SMBus(0) # For revision 1 Raspberry Pi 144 PyTell.debug("I2C at bus 0 detected") 145 except: 146 PyTell.debug("Failed to detect I2C bus.") 147 148 if self._isReady: 149 self._startPos = -1 # no text yet 150 self.clear()
151 152 # ----------------------- Methods ---------------------- 153
154 - def getId(self):
155 ''' 156 Returns the Id number of the display (default: 0xA0). 157 @return: the display's id; None if display is not ready or I2C transfer failed 158 ''' 159 return self.readByte()
160
161 - def readByte(self):
162 ''' 163 Send the I2C address without a command and returns the byte received. 164 @return: one byte reply; None if display not ready or I2C transfer failed 165 ''' 166 if not self._isReady: 167 return None 168 try: 169 rc = self._bus.read_byte(self.i2c_address) 170 return rc 171 except: 172 return None
173 174
175 - def readData(self, cmd):
176 ''' 177 Sends a command and returns the block of data received. 178 @param cmd: the command to send 179 @return: reply as list; None if display not ready or I2C transfer failed 180 ''' 181 if not self._isReady: 182 return None 183 try: 184 rc = self._bus.read_block_data(self.i2c_address, cmd) 185 return rc 186 except: 187 return None
188
189 - def writeData(self, data):
190 ''' 191 Sends 4 data bytes to the display 192 @param data list or tuple of integers whose lower byte are used (higher bytes 193 are ignored). 194 ''' 195 if not self._isReady: 196 return 197 if not (type(data) == list or type(data) == tuple) or len(data) != 4: 198 raise Exception("Error in PyTell.writeData():\nWrong parameter type.") 199 for v in data: 200 if not (type(v) == int): 201 raise Exception("Error in PyTell.writeData():\nWrong parameter type.") 202 try: 203 cmd = 1 # Segment mode 204 PyTell.debug("bus.write_block_data(" + str(self.i2c_address) + "," + str(cmd) + "," + str(data) + ")") 205 self._bus.write_block_data(self.i2c_address, cmd, data) 206 except IOError, err: 207 raise Exception("PyTell.writeData(). Can't access device at address 0x%02X" % self.i2c_address)
208
209 - def clear(self):
210 ''' 211 Clears the display (all digits are turned off). 212 ''' 213 PyTell.debug("Calling clear()") 214 if not self._isReady: 215 return 216 self.writeData([0, 0, 0, 0])
217
218 - def showText(self, text, pos = 0, dp = [0, 0, 0, 0]):
219 ''' 220 Displays 4 characters of the given text. The text is considered to be prefixed and postfixed by spaces 221 and the 4 character window is selected by the text pointer pos that determines the character displayed at the 222 leftmost digit, e.g. (_: empty): 223 showText("AbCdEF") -> AbCd 224 showText("AbCdEF", 1) -> bCdE 225 showText("AbCdEF", -1) ->_AbC 226 showText("AbCdEF", 4) -> EF__ 227 @param text: the text to display (list, tuple, string or integer) 228 @param pos: the start value of the text pointer (character index positioned a leftmost digit) 229 @param dp: a list with one to four 1 or 0, if the decimal point is shown or not. For compatibility with 230 the 4tronix display, the following mapping is used: 231 The first element in list corresponds to dp at second digit from the right, the second element to dp 232 at third digit from the right, the third element to dp at leftmost digit, the forth element to the dp at 233 rightmost digit. More than 4 elements are ignored 234 @return: True, if successful; False, if the display is not available, 235 text or dp has illegal type or one of the characters can't be displayed 236 ''' 237 PyTell.debug("showText(" + str(text) + "," + str(pos) + "," + str(dp) + ")") 238 if not self._isReady: 239 return False 240 if not (type(text) == int or type(text) == list or type(text) == tuple or type(text) == str): 241 return False 242 if not (type(dp) == list or type(dp) == tuple): 243 return False 244 self._startPos = pos 245 self._pos = pos 246 dpList = [0] * 4 247 for i in range(min(4, len(dp))): 248 dpList[i] = dp[i] 249 self._decimalPoint = [0] * 4 250 self._decimalPoint[0] = dpList[2] 251 self._decimalPoint[1] = dpList[1] 252 self._decimalPoint[2] = dpList[0] 253 self._decimalPoint[3] = dpList[3] 254 text = str(text) # convert digits to chars 255 self._text = [' '] * len(text) 256 for i in range(len(text)): 257 try: 258 self._text[i] = PyTell.PATTERN[text[i]] 259 except: 260 self._text[i] = 0 # empty 261 data = self._getData(self._pos) 262 self.writeData(data) 263 return True
264
265 - def scrollToLeft(self):
266 ''' 267 Scrolls the current text one step to the left by increasing the text pointer. 268 @return: the number of characters hidden, but remaining to be displayed at the right (>=0); -1, if error 269 ''' 270 if not self._isReady: 271 return -1 272 if self._startPos == -1: # no text yet 273 return -1 274 self._pos += 1 275 data = self._getData(self._pos) 276 self.writeData(data) 277 nb = len(self._text) - self._pos 278 return max(0, nb)
279
280 - def scrollToRight(self):
281 ''' 282 Scrolls the current text one step to the left by decreasing the text pointer. 283 @return: the number of characters hidden, but remaining to be displayed at the left (>=0); -1, if error 284 ''' 285 if not self._isReady: 286 return -1 287 if self._startPos == -1: # no text yet 288 return -1 289 self._pos -= 1 290 pos = self._pos 291 data = self._getData(self._pos) 292 self.writeData(data) 293 return max(0, self._pos)
294
295 - def setToStart(self):
296 ''' 297 Shows the scrollable text at the start position by setting the text pointer to its start value. 298 @return: 0, if successful; -1, if error 299 ''' 300 if not self._isReady: 301 return -1 302 if self._startPos == -1: # no text yet 303 return -1 304 self._pos = self._startPos 305 data = self._getData(self._pos) 306 self.writeData(data) 307 return 0
308
309 - def showTicker(self, text, count = 1, speed = 2, blocking = False):
310 ''' 311 Shows a ticker text that scroll to the left until the last 4 characters are displayed. The method blocks 312 until the ticker thread is successfully started and isTickerAlive() returns True. 313 @param text: the text to display, if short than 4 characters, scrolling is disabled 314 @param count: the number of repetitions (default: 1). For count = 0, infinite duration, 315 may be stopped by calling stopTicker(). 316 @param speed: the speed number of scrolling operations per sec (default: 2) 317 @param blocking: if True, the method blocks until the ticker has finished; otherwise 318 it returns immediately (default: False) 319 ''' 320 if not self._isReady: 321 return 322 self.clear(); 323 if self._tickerThread != None: 324 self.stopTicker() 325 if self._blinkerThread != None: 326 self.stopBlinker() 327 self._tickerThread = TickerThread(self, text, count, speed) 328 if blocking: 329 while self.isTickerAlive(): 330 continue
331
332 - def stopTicker(self):
333 ''' 334 Stops a running ticker. 335 The method blocks until the ticker thread is finished and isTickerAlive() returns False. 336 ''' 337 if not self._isReady: 338 return 339 if self._tickerThread != None: 340 self._tickerThread.stop() 341 self._tickerThread = None
342
343 - def isTickerAlive(self):
344 ''' 345 @return: True, if the ticker is displaying; otherwise False 346 ''' 347 if not self._isReady: 348 return False 349 PyTell.delay(1) 350 if self._tickerThread == None: 351 return False 352 return self._tickerThread._isAlive
353
354 - def showBlinker(self, text, dp = [0, 0, 0, 0], count = 3, speed = 1, blocking = False):
355 ''' 356 Shows a ticker text that scroll to the left until the last 4 characters are displayed. The method blocks 357 until the ticker thread is successfully started and isTickerAlive() returns True. 358 @param text: the text to display, if short than 4 characters, scrolling is disabled 359 @param count: the number of repetitions (default: 2). For count = 0, infinite duration, 360 may be stopped by calling stopBlinker(). 361 @param speed: the speed number of blinking operations per sec (default: 1) 362 @param blocking: if True, the method blocks until the blinker has finished; otherwise 363 it returns immediately (default: False) 364 ''' 365 if not self._isReady: 366 return 367 self.clear(); 368 if self._tickerThread != None: 369 self.stopTicker() 370 if self._blinkerThread != None: 371 self.stopBlinker() 372 self._blinkerThread = BlinkerThread(self, text, dp, count, speed) 373 if blocking: 374 while self.isBlinkerAlive(): 375 continue
376
377 - def stopBlinker(self):
378 ''' 379 Stops a running blinker. 380 The method blocks until the blinker thread is finished and isBlinkerAlive() returns False. 381 ''' 382 if not self._isReady: 383 return 384 if self._blinkerThread != None: 385 self._blinkerThread.stop() 386 self._blinkerThread = None
387
388 - def isBlinkerAlive(self):
389 ''' 390 @return: True, if the blinker is displaying; otherwise False 391 ''' 392 if not self._isReady: 393 return False 394 PyTell.delay(1) 395 if self._blinkerThread == None: 396 return False 397 return self._blinkerThread._isAlive
398
399 - def showVersion(self):
400 ''' 401 Displays current version. Format X (three horz bars) + n.nn 402 ''' 403 v = "X" + PyTell.VERSION.replace(".", "") 404 self.showText(v, pos = 0, dp = [0, 1])
405
406 - def isAvailable(self):
407 ''' 408 Check if device is available. 409 @return: True, if device can be accessed 410 ''' 411 return self._isReady
412 413 # -------------------- private methods ----------------------------
414 - def _getData(self, pos):
415 if pos >= 0: 416 data = self._text[pos:pos + 4] 417 for i in range(4 - len(data)): 418 data.append(0) # spaces 419 else: 420 if 4 + pos >= 0: 421 data = self._text[0:4 + pos] 422 else: 423 data = [] 424 data.reverse() 425 for i in range(4 - len(data)): 426 data.append(0) # spaces 427 data.reverse() 428 for i in range(4): 429 data[i] = data[i] + 128 * self._decimalPoint[i] # add decimal points 430 return data
431
432 # ------------------- class TickerThread ---------------------- 433 -class TickerThread(Thread):
434 - def __init__(self, display, text, count, speed):
435 Thread.__init__(self) 436 self._text = text 437 self._display = display 438 if speed <= 0: 439 speed = 1 440 self._period = int(1000.0 / speed) 441 self._count = count 442 self._isRunning = False 443 self._isAlive = True 444 self.start() 445 while not self._isRunning: 446 continue
447
448 - def run(self):
449 PyTell.debug("TickerThread started") 450 self._display.showText(self._text) 451 nb = 0 452 self._isRunning = True 453 while self._isRunning: 454 startTime = time.time() 455 while time.time() - startTime < self._period / 1000.0 and self._isRunning: 456 PyTell.delay(1) 457 if not self._isRunning: 458 break 459 rc = self._display.scrollToLeft() 460 if rc == 4 and self._isRunning: 461 startTime = time.time() 462 while time.time() - startTime < 2 and self._isRunning: 463 PyTell.delay(10) 464 if not self._isRunning: 465 break 466 nb += 1 467 if nb == self._count: 468 break 469 self._display.setToStart() 470 if self._isRunning: # terminated by number of count 471 while time.time() - startTime < 2 and self._isRunning: 472 PyTell.delay(10) 473 self._display.clear() 474 PyTell.debug("TickerThread terminated") 475 self._isAlive = False
476
477 - def stop(self):
478 self._isRunning = False 479 while self._isAlive: # Wait until thread is finished 480 continue 481 PyTell.debug("Clearing display") 482 self._display.clear()
483
484 485 # ------------------- class BlinkerThread ---------------------- 486 -class BlinkerThread(Thread):
487 - def __init__(self, display, text, dp, count, speed):
488 Thread.__init__(self) 489 self._text = text 490 self._dp = dp 491 self._display = display 492 if speed <= 0: 493 speed = 1 494 self._period = int(1000.0 / speed) 495 self._count = count 496 self._isRunning = False 497 self._isAlive = True 498 self.start() 499 while not self._isRunning: 500 continue
501
502 - def run(self):
503 PyTell.debug("BlinkerThread started") 504 nb = 0 505 self._isRunning = True 506 while self._isRunning: 507 self._display.showText(self._text, dp = self._dp) 508 startTime = time.time() 509 while time.time() - startTime < self._period / 1000.0 and self._isRunning: 510 PyTell.delay(1) 511 if self._isRunning == False: 512 break 513 self._display.clear() 514 startTime = time.time() 515 while time.time() - startTime < self._period / 1000.0 and self._isRunning: 516 PyTell.delay(1) 517 if self._isRunning == False: 518 break 519 nb += 1 520 if nb == self._count: 521 self._isRunning = False 522 523 startTime = time.time() 524 while time.time() - startTime < 1: 525 PyTell.delay(1) 526 PyTell.debug("BlinkerThread terminated") 527 self._isAlive = False
528
529 - def stop(self):
530 self._isRunning = False 531 while self._isAlive: # Wait until thread is finished 532 continue
533