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

Source Code for Module Disp4tronix

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