1
2
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
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
69 @staticmethod
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
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
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
104
105 @staticmethod
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
126 time.sleep(interval / 1000.0)
127
128
151
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
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)
192 self._bus.write_byte_data(Disp4tronix.i2c_address, 0x12, value)
193 return True
194
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
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
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)
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)
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
286
301
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:
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
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
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
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
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
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
412 if self._bus != None:
413 return True
414 return False
415
416
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(' ')
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(' ')
433 data.reverse()
434 r = ''.join(data)
435 return r
436
441 Thread.__init__(self)
442 self._display = display
443 self._isRunning = False
444 self._isAlive = False
445
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
473 self._isRunning = False
474 while self._isAlive:
475 continue
476
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
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:
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
523 self._isRunning = False
524 while self._isAlive:
525 continue
526 Tools.debug("Clearing display")
527 self._display.clear()
528
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
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()
571 Disp4tronix.debug("BlinkerThread terminated")
572 self._isAlive = False
573
575 self._isRunning = False
576 while self._isAlive:
577 continue
578