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