1
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
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 DgTell1.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 = 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)
139 DgTell1.debug("I2C at bus 1 detected")
140 else:
141 self._bus = SMBus(0)
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
148 self.clear()
149 DgTell1._myInstance = self
150
151
152
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
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
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
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
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)
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
240 data = self._getData(self._pos)
241 self.writeData(data)
242 return True
243
258
273
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:
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
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
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
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
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
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
385 '''
386 Check if device is available.
387 @return: True, if device can be accessed
388 '''
389 return self._isReady
390
391
393 if pos >= 0:
394 data = self._text[pos:pos + 4]
395 for i in range(4 - len(data)):
396 data.append(0)
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)
405 data.reverse()
406 for i in range(4):
407 data[i] = data[i] + 128 * self._decimalPoint[i]
408 return data
409
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
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:
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
456 self._isRunning = False
457 while self._isAlive:
458 continue
459 DgTell1.debug("Clearing display")
460 self._display.clear()
461
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
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
508 self._isRunning = False
509 while self._isAlive:
510 continue
511