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

Source Code for Module DgTell

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