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

Source Code for Module DgTell1

  1  # DgTell1.py 
  2   
  3  ''' 
  4  Class that represents VERSION 1 of 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 DgTell1 ---------------------- 22 -class DgTell1():
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 DgTell1.DEBUG: 67 print "DgTell1 debug->", msg
68 69 @staticmethod
70 - def getVersion():
71 return DgTell1.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 DgTell1.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 = 0x20):
125 ''' 126 Creates a display instance with display set to given i2c address (default: 0x20). 127 Then the display is cleared. 128 @param i2c_address: the i2c address (default: 0x20) 129 ''' 130 self.i2c_address = i2c_address 131 self._bus = None 132 self._isReady = True 133 self._tickerThread = None 134 self._blinkerThread = None 135 136 try: 137 if GPIO.RPI_REVISION > 1: 138 self._bus = SMBus(1) # For revision 2 Raspberry Pi 139 DgTell1.debug("I2C at bus 1 detected") 140 else: 141 self._bus = SMBus(0) # For revision 1 Raspberry Pi 142 DgTell1.debug("I2C at bus 0 detected") 143 except: 144 DgTell1.debug("Failed to detect I2C bus.") 145 146 if self._isReady: 147 self._startPos = -1 # no text yet 148 self.clear() 149 DgTell1._myInstance = self
150 151 # ----------------------- Methods ---------------------- 152
153 - def writeData(self, data):
154 ''' 155 Sends 4 data bytes to the display 156 @param data list or tuple of integers whose lower byte are used (higher bytes 157 are ignored). 158 ''' 159 if not self._isReady: 160 return 161 if not (type(data) == list or type(data) == tuple) or len(data) != 4: 162 raise Exception("Error in DgTell1.writeData():\nWrong parameter type.") 163 for v in data: 164 if not (type(v) == int): 165 raise Exception("Error in DgTell1.writeData():\nWrong parameter type.") 166 try: 167 cmd = 1 # Segment mode 168 DgTell1.debug("bus.write_block_data(" + str(self.i2c_address) + "," + str(cmd) + "," + str(data) + ")") 169 self._bus.write_block_data(self.i2c_address, cmd, data) 170 except IOError, err: 171 raise Exception("DgTell1.writeData(). Can't access device at address 0x%02X" % self.i2c_address)
172
173 - def clear(self):
174 ''' 175 Clears the display (all digits are turned off). 176 ''' 177 DgTell1.debug("Calling clear()") 178 if not self._isReady: 179 return 180 self.writeData([0, 0, 0, 0])
181
182 - def showTwoBytes(self, high, low):
183 ''' 184 Displays the two bytes (0..255) at low and high position. 185 @param low: integer 0..255 shown at right position 186 @param high: integer 0..255 shown at left position 187 ''' 188 if not self._isReady: 189 return 190 try: 191 cmd = 3 # Hex mode 192 DgTell1.debug("bus.write_block_data(" + str(self.i2c_address) + "," + str(cmd) + "," + str(high) + "," + str(low) + ")") 193 self._bus.write_block_data(self.i2c_address, cmd, [high, low]) 194 except IOError, err: 195 raise Exception("DgTell1.writeData(). Can't access device at address 0x%02X" % self.i2c_address)
196
197 - def showText(self, text, pos = 0, dp = [0, 0, 0, 0]):
198 ''' 199 Displays 4 characters of the given text. The text is considered to be prefixed and postfixed by spaces 200 and the 4 character window is selected by the text pointer pos that determines the character displayed at the 201 leftmost digit, e.g. (_: empty): 202 showText("AbCdEF") -> AbCd 203 showText("AbCdEF", 1) -> bCdE 204 showText("AbCdEF", -1) ->_AbC 205 showText("AbCdEF", 4) -> EF__ 206 @param text: the text to display (list, tuple, string or integer) 207 @param pos: the start value of the text pointer (character index positioned a leftmost digit) 208 @param dp: a list with one to four 1 or 0, if the decimal point is shown or not. For compatibility with 209 the 4tronix display, the following mapping is used: 210 The first element in list corresponds to dp at second digit from the right, the second element to dp 211 at third digit from the right, the third element to dp at leftmost digit, the forth element to the dp at 212 rightmost digit. More than 4 elements are ignored 213 @return: True, if successful; False, if the display is not available, 214 text or dp has illegal type or one of the characters can't be displayed 215 ''' 216 DgTell1.debug("showText(" + str(text) + "," + str(pos) + "," + str(dp) + ")") 217 if not self._isReady: 218 return False 219 if not (type(text) == int or type(text) == list or type(text) == tuple or type(text) == str): 220 return False 221 if not (type(dp) == list or type(dp) == tuple): 222 return False 223 self._startPos = pos 224 self._pos = pos 225 dpList = [0] * 4 226 for i in range(min(4, len(dp))): 227 dpList[i] = dp[i] 228 self._decimalPoint = [0] * 4 229 self._decimalPoint[0] = dpList[2] 230 self._decimalPoint[1] = dpList[1] 231 self._decimalPoint[2] = dpList[0] 232 self._decimalPoint[3] = dpList[3] 233 text = str(text) # convert digits to chars 234 self._text = [' '] * len(text) 235 for i in range(len(text)): 236 try: 237 self._text[i] = DgTell1.PATTERN[text[i]] 238 except: 239 self._text[i] = 0 # empty 240 data = self._getData(self._pos) 241 self.writeData(data) 242 return True
243
244 - def scrollToLeft(self):
245 ''' 246 Scrolls the current text one step to the left by increasing the text pointer. 247 @return: the number of characters hidden, but remaining to be displayed at the right (>=0); -1, if error 248 ''' 249 if not self._isReady: 250 return -1 251 if self._startPos == -1: # no text yet 252 return -1 253 self._pos += 1 254 data = self._getData(self._pos) 255 self.writeData(data) 256 nb = len(self._text) - self._pos 257 return max(0, nb)
258
259 - def scrollToRight(self):
260 ''' 261 Scrolls the current text one step to the left by decreasing the text pointer. 262 @return: the number of characters hidden, but remaining to be displayed at the left (>=0); -1, if error 263 ''' 264 if not self._isReady: 265 return -1 266 if self._startPos == -1: # no text yet 267 return -1 268 self._pos -= 1 269 pos = self._pos 270 data = self._getData(self._pos) 271 self.writeData(data) 272 return max(0, self._pos)
273
274 - def setToStart(self):
275 ''' 276 Shows the scrollable text at the start position by setting the text pointer to its start value. 277 @return: 0, if successful; -1, if error 278 ''' 279 if not self._isReady: 280 return -1 281 if self._startPos == -1: # no text yet 282 return -1 283 self._pos = self._startPos 284 data = self._getData(self._pos) 285 self.writeData(data) 286 return 0
287
288 - def showTicker(self, text, count = 1, speed = 2, blocking = False):
289 ''' 290 Shows a ticker text that scroll to the left until the last 4 characters are displayed. The method blocks 291 until the ticker thread is successfully started and isTickerAlive() returns True. 292 @param text: the text to display, if short than 4 characters, scrolling is disabled 293 @param count: the number of repetitions (default: 1). For count = 0, infinite duration, 294 may be stopped by calling stopTicker(). 295 @param speed: the speed number of scrolling operations per sec (default: 2) 296 @param blocking: if True, the method blocks until the ticker has finished; otherwise 297 it returns immediately (default: False) 298 ''' 299 if not self._isReady: 300 return 301 self.clear(); 302 if self._tickerThread != None: 303 self.stopTicker() 304 if self._blinkerThread != None: 305 self.stopBlinker() 306 self._tickerThread = TickerThread(self, text, count, speed) 307 if blocking: 308 while self.isTickerAlive(): 309 continue
310
311 - def stopTicker(self):
312 ''' 313 Stops a running ticker. 314 The method blocks until the ticker thread is finished and isTickerAlive() returns False. 315 ''' 316 if not self._isReady: 317 return 318 if self._tickerThread != None: 319 self._tickerThread.stop() 320 self._tickerThread = None
321
322 - def isTickerAlive(self):
323 ''' 324 @return: True, if the ticker is displaying; otherwise False 325 ''' 326 if not self._isReady: 327 return False 328 DgTell1.delay(1) 329 if self._tickerThread == None: 330 return False 331 return self._tickerThread._isAlive
332
333 - def showBlinker(self, text, dp = [0, 0, 0, 0], count = 3, speed = 1, blocking = False):
334 ''' 335 Shows a blinking text for the given number of times and blinking speed. 336 @param text: the text to display, if short than 4 characters, scrolling is disabled 337 @param count: the number of repetitions (default: 3). For count = 0, infinite duration, 338 may be stopped by calling stopBlinker(). 339 @param speed: the speed number of blinking operations per sec (default: 1) 340 @param blocking: if True, the method blocks until the blinker has finished; otherwise 341 it returns immediately (default: False) 342 ''' 343 if not self._isReady: 344 return 345 self.clear(); 346 if self._tickerThread != None: 347 self.stopTicker() 348 if self._blinkerThread != None: 349 self.stopBlinker() 350 self._blinkerThread = BlinkerThread(self, text, dp, count, speed) 351 if blocking: 352 while self.isBlinkerAlive(): 353 continue
354
355 - def stopBlinker(self):
356 ''' 357 Stops a running blinker. 358 The method blocks until the blinker thread is finished and isBlinkerAlive() returns False. 359 ''' 360 if not self._isReady: 361 return 362 if self._blinkerThread != None: 363 self._blinkerThread.stop() 364 self._blinkerThread = None
365
366 - def isBlinkerAlive(self):
367 ''' 368 @return: True, if the blinker is displaying; otherwise False 369 ''' 370 if not self._isReady: 371 return False 372 DgTell1.delay(1) 373 if self._blinkerThread == None: 374 return False 375 return self._blinkerThread._isAlive
376
377 - def showVersion(self):
378 ''' 379 Displays current version. Format X (three horz bars) + n.nn 380 ''' 381 v = "X" + DgTell1.VERSION.replace(".", "") 382 self.showText(v, pos = 0, dp = [0, 1])
383
384 - def isAvailable(self):
385 ''' 386 Check if device is available. 387 @return: True, if device can be accessed 388 ''' 389 return self._isReady
390 391 # -------------------- private methods ----------------------------
392 - def _getData(self, pos):
393 if pos >= 0: 394 data = self._text[pos:pos + 4] 395 for i in range(4 - len(data)): 396 data.append(0) # spaces 397 else: 398 if 4 + pos >= 0: 399 data = self._text[0:4 + pos] 400 else: 401 data = [] 402 data.reverse() 403 for i in range(4 - len(data)): 404 data.append(0) # spaces 405 data.reverse() 406 for i in range(4): 407 data[i] = data[i] + 128 * self._decimalPoint[i] # add decimal points 408 return data
409
410 # ------------------- class TickerThread ---------------------- 411 -class TickerThread(Thread):
412 - def __init__(self, display, text, count, speed):
413 Thread.__init__(self) 414 self._text = text 415 self._display = display 416 if speed <= 0: 417 speed = 1 418 self._period = int(1000.0 / speed) 419 self._count = count 420 self._isRunning = False 421 self._isAlive = True 422 self.start() 423 while not self._isRunning: 424 continue
425
426 - def run(self):
427 DgTell1.debug("TickerThread started") 428 self._display.showText(self._text) 429 nb = 0 430 self._isRunning = True 431 while self._isRunning: 432 startTime = time.time() 433 while time.time() - startTime < self._period / 1000.0 and self._isRunning: 434 DgTell1.delay(1) 435 if not self._isRunning: 436 break 437 rc = self._display.scrollToLeft() 438 if rc == 4 and self._isRunning: 439 startTime = time.time() 440 while time.time() - startTime < 2 and self._isRunning: 441 DgTell1.delay(10) 442 if not self._isRunning: 443 break 444 nb += 1 445 if nb == self._count: 446 break 447 self._display.setToStart() 448 if self._isRunning: # terminated by number of count 449 while time.time() - startTime < 2 and self._isRunning: 450 DgTell1.delay(10) 451 self._display.clear() 452 DgTell1.debug("TickerThread terminated") 453 self._isAlive = False
454
455 - def stop(self):
456 self._isRunning = False 457 while self._isAlive: # Wait until thread is finished 458 continue 459 DgTell1.debug("Clearing display") 460 self._display.clear()
461
462 463 # ------------------- class BlinkerThread ---------------------- 464 -class BlinkerThread(Thread):
465 - def __init__(self, display, text, dp, count, speed):
466 Thread.__init__(self) 467 self._text = text 468 self._dp = dp 469 self._display = display 470 if speed <= 0: 471 speed = 1 472 self._period = int(1000.0 / speed) 473 self._count = count 474 self._isRunning = False 475 self._isAlive = True 476 self.start() 477 while not self._isRunning: 478 continue
479
480 - def run(self):
481 DgTell1.debug("BlinkerThread started") 482 nb = 0 483 self._isRunning = True 484 while self._isRunning: 485 self._display.showText(self._text, dp = self._dp) 486 startTime = time.time() 487 while time.time() - startTime < self._period / 1000.0 and self._isRunning: 488 DgTell1.delay(1) 489 if self._isRunning == False: 490 break 491 self._display.clear() 492 startTime = time.time() 493 while time.time() - startTime < self._period / 1000.0 and self._isRunning: 494 DgTell1.delay(1) 495 if self._isRunning == False: 496 break 497 nb += 1 498 if nb == self._count: 499 self._isRunning = False 500 501 startTime = time.time() 502 while time.time() - startTime < 1: 503 DgTell1.delay(1) 504 DgTell1.debug("BlinkerThread terminated") 505 self._isAlive = False
506
507 - def stop(self):
508 self._isRunning = False 509 while self._isAlive: # Wait until thread is finished 510 continue
511