1
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
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
64 @staticmethod
68
69 @staticmethod
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
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
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
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
121 time.sleep(timeout / 1000.0)
122
123
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)
140 DgTell.debug("I2C at bus 1 detected")
141 else:
142 self._bus = SMBus(0)
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
157 self.clear()
158 DgTell._myInstance = self
159
160
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
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
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
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
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
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:
253 self.writeData([32, 32, 32, 32])
254
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])
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)
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
309 data = self._getData(self._pos)
310 self.writeData(data)
311 return True
312
327
342
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:
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
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
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
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
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
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
454 '''
455 Check if device is available.
456 @return: True, if device can be accessed
457 '''
458 return self._isReady
459
460
462 if pos >= 0:
463 data = self._text[pos:pos + 4]
464 for i in range(4 - len(data)):
465 data.append(0)
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)
474 data.reverse()
475 for i in range(4):
476 data[i] = data[i] + 128 * self._decimalPoint[i]
477 return data
478
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
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:
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
525 self._isRunning = False
526 while self._isAlive:
527 continue
528 DgTell.debug("Clearing display")
529 self._display.clear()
530
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
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
577 self._isRunning = False
578 while self._isAlive:
579 continue
580