diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..31f4609 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '18 4 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..4e1ef42 --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,31 @@ +# This workflows will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe1398e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.swp +__pycache__ +/.idea/ +/venv/ +/build/ +/dist/ +/raspberrypi_tm1637.egg-info/ diff --git a/README.md b/README.md index 85860d4..62378a7 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,27 @@ -# MicroPython TM1637 +# TM1637 +# Raspberry Pi Python 3 TM1637 -A MicroPython library for quad 7-segment LED display modules using the TM1637 LED driver. +A Python 3 port from MicroPython library for the quad 7-segment LED display modules based on the TM1637 LED driver, implemented on Raspberry Pi. For example, the [Grove - 4 Digit Display module](http://wiki.seeed.cc/Grove-4-Digit_Display/) -![demo](docs/demo.jpg) +![demo](https://github.com/depklyon/raspberrypi-tm1637/raw/master/docs/raspberry_tm1637.gif) -## Examples +## Installation -Copy the file to your device, using ampy, webrepl or compiling and deploying. eg. +This project is available through [pip](https://www.w3schools.com/python/python_pip.asp). Make sure that you are using Python 3. ``` -$ ampy put tm1637.py +$ pip3 install raspberrypi-tm1637 ``` +## Examples + **Basic usage** ```python import tm1637 -from machine import Pin -tm = tm1637.TM1637(clk=Pin(5), dio=Pin(4)) +tm = tm1637.TM1637(clk=5, dio=4) # all LEDS on "88:88" tm.write([127, 255, 127, 127]) @@ -50,7 +52,7 @@ tm.number(-123) tm.temperature(24) ``` -For more detailed examples, see [tm1637_test.py](tm1637_test.py) +For more detailed examples, see [demo.py](https://github.com/depklyon/raspberrypi-tm1637/blob/master/demo.py) # Seven Segment Font @@ -160,7 +162,7 @@ temperature(num) ``` Show a string on the display. -Shorthand for write(encode_string()). +Shorthand for `write(encode_string())`. Limited to first 4 characters. ```python show(string, colon=False) @@ -174,29 +176,23 @@ scroll(string, delay=250) ## Parts -* [WeMos D1 Mini](https://www.aliexpress.com/store/product/D1-mini-Mini-NodeMcu-4M-bytes-Lua-WIFI-Internet-of-Things-development-board-based-ESP8266/1331105_32529101036.html) $4.00 USD * [Grove 4 Digit Display](https://www.seeedstudio.com/grove-4digital-display-p-1198.html) $5.90 USD * [Grove Male Jumper Cable](https://www.seeedstudio.com/Grove-4-pin-Male-Jumper-to-Grove-4-pin-Conversion-Cable-%285-PCs-per-Pack%29-p-1565.html) $2.90 USD ## Connections -WeMos D1 Mini | 4 Digit Display +Raspberry Pi | 4 Digit Display ------------- | --------------- -D1 (GPIO5) | CLK -D2 (GPIO4) | DIO +GPIO5 | CLK +GPIO4 | DIO 3V3 (or 5V) | VCC -G | GND +GND | GND ## Links -* [WeMos D1 Mini](https://wiki.wemos.cc/products:d1:d1_mini) -* [micropython.org](http://micropython.org) * [TM1637 datasheet](http://www.titanmec.com/index.php/en/project/download/id/302.html) -* [Titan Micro TM1637 product page](http://www.titanmec.com/index.php/en/project/view/id/302.html) * [Nokia 5110 version](https://github.com/mcauser/MicroPython-ESP8266-Nokia-5110-Quad-7-segment) * [BBC micro:bit version](https://github.com/mcauser/microbit-tm1637) -* [Adafruit Ampy](https://learn.adafruit.com/micropython-basics-load-files-and-run-code/install-ampy) -* [micropython-tm1637 on pypi](https://pypi.python.org/pypi/micropython-tm1637/) ## License diff --git a/tm1637_test.py b/demo.py similarity index 70% rename from tm1637_test.py rename to demo.py index 248f02b..b5d7d06 100644 --- a/tm1637_test.py +++ b/demo.py @@ -1,62 +1,79 @@ -# MicroPython TM1637 quad 7-segment LED display driver examples +#!/usr/bin/env python3 -# WeMos D1 Mini -- 4 Digit Display -# D1 (GPIO5) ----- CLK -# D2 (GPIO4) ----- DIO -# 3V3 ------------ VCC -# G -------------- GND +# Raspberry Pi Python 3 TM1637 quad 7-segment LED display driver examples +from time import sleep import tm1637 -from machine import Pin -tm = tm1637.TM1637(clk=Pin(5), dio=Pin(4)) -# all LEDS on "88:88" +CLK = 1 +DIO = 0 +DELAY = 0.5 + +tm = tm1637.TM1637(clk=CLK, dio=DIO) + +# all segments on "88:88" tm.write([127, 255, 127, 127]) tm.write(bytearray([127, 255, 127, 127])) tm.write(b'\x7F\xFF\x7F\x7F') tm.show('8888', True) tm.numbers(88, 88, True) +sleep(DELAY) -# all LEDS off +# all segments off tm.write([0, 0, 0, 0]) tm.show(' ') +sleep(DELAY) # write to the 2nd and 3rd segments only -tm.write([119, 124], 1) # _Ab_ -tm.write([124], 2) # __b_ -tm.write([119], 1) # _A__ +tm.write([119, 124], 1) # _Ab_ +sleep(DELAY) +tm.write([0, 0, 0, 0]) +tm.write([124], 2) # __b_ +sleep(DELAY) +tm.write([119], 1) # _A__ +sleep(DELAY) # display "0123" tm.write([63, 6, 91, 79]) tm.write(bytearray([63, 6, 91, 79])) tm.write(b'\x3F\x06\x5B\x4F') +sleep(DELAY) tm.show('1234') tm.number(1234) tm.numbers(12, 34, False) +sleep(DELAY) # display "4567" tm.write([102, 109, 125, 7]) -tm.write([102], 0) # 4___ -tm.write([109], 1) # _5__ -tm.write([125], 2) # __6_ -tm.write([7], 3) # ___7 +sleep(DELAY) +tm.write([0, 0, 0, 0]) +tm.write([102], 0) # 4___ +sleep(DELAY) +tm.write([109], 1) # _5__ +sleep(DELAY) +tm.write([125], 2) # __6_ +sleep(DELAY) +tm.write([7], 3) # ___7 +sleep(DELAY) # set middle two segments to "12", ie "4127" -tm.write([6, 91], 1) # _12_ +tm.write([6, 91], 1) # _12_ +sleep(DELAY) # set last segment to "9", ie "4129" -tm.write([111], 3) # ___9 +tm.write([111], 3) # ___9 +sleep(DELAY) # walk through all possible LED combinations -from time import sleep_ms for i in range(128): tm.number(i) tm.write([i]) - sleep_ms(100) + sleep(0.1) # show "AbCd" tm.write([119, 124, 57, 94]) tm.show('abcd') +sleep(DELAY) # show "COOL" tm.write([0b00111001, 0b00111111, 0b00111111, 0b00111000]) @@ -65,38 +82,48 @@ tm.write([57, 63, 63, 56]) tm.show('cool') tm.show('COOL') +sleep(DELAY) # display "dEAd", "bEEF" tm.hex(0xdead) +sleep(DELAY) tm.hex(0xbeef) +sleep(DELAY) tm.show('dead') +sleep(DELAY) tm.show('Beef') +sleep(DELAY) # show "12:59" tm.numbers(12, 59) tm.show('1259', True) +sleep(DELAY) # show "-123" tm.number(-123) tm.show('-123') +sleep(DELAY) # Show Help tm.show('Help') tm.write(tm.encode_string('Help')) tm.write([tm.encode_char('H'), tm.encode_char('e'), tm.encode_char('l'), tm.encode_char('p')]) +sleep(DELAY) # Scroll Hello World from right to left -tm.scroll('Hello World') # 4 fps -tm.scroll('Hello World', 1000) # 1 fps +tm.scroll('Hello World') # 4 fps +tm.scroll('Hello World', 1000) # 1 fps # Scroll all available characters tm.scroll(list(tm1637._SEGMENTS)) # all LEDs dim tm.brightness(0) +sleep(DELAY) # all LEDs bright tm.brightness(7) +sleep(DELAY) # converts a digit 0-0x0f to a byte representing a single segment # use write() to render the byte on a single segment @@ -159,56 +186,80 @@ # display "dEAd", "bEEF", "CAFE" and "bAbE" tm.hex(0xdead) +sleep(DELAY) tm.hex(0xbeef) +sleep(DELAY) tm.hex(0xcafe) +sleep(DELAY) tm.hex(0xbabe) +sleep(DELAY) # show "00FF" (hex right aligned) tm.hex(0xff) +sleep(DELAY) # show " 1" (numbers right aligned) tm.number(1) +sleep(DELAY) # show " 12" tm.number(12) +sleep(DELAY) # show " 123" tm.number(123) +sleep(DELAY) # show "9999" capped at 9999 tm.number(20000) +sleep(DELAY) # show " -1" tm.number(-1) +sleep(DELAY) # show " -12" tm.number(-12) +sleep(DELAY) # show "-123" tm.number(-123) +sleep(DELAY) # show "-999" capped at -999 tm.number(-1234) +sleep(DELAY) # show "01:02" tm.numbers(1, 2) +sleep(DELAY) # show "0102" tm.numbers(1, 2, False) +sleep(DELAY) # show "-5:11" tm.numbers(-5, 11) +sleep(DELAY) # show "12:59" tm.numbers(12, 59) +sleep(DELAY) # show temperature '24*C' tm.temperature(24) +sleep(DELAY) tm.show('24*C') +sleep(DELAY) # show temperature works for range -9 to +99 -tm.temperature(-10) # LO*C +tm.temperature(-10) # LO*C +sleep(DELAY) tm.temperature(-9) # -9*C -tm.temperature(5) # 5*C +sleep(DELAY) +tm.temperature(5) # 5*C +sleep(DELAY) tm.temperature(99) # 99*C -tm.temperature(100) # HI*C +sleep(DELAY) +tm.temperature(100) # HI*C +sleep(DELAY) diff --git a/docs/demo.jpg b/docs/demo.jpg deleted file mode 100644 index 68e594d..0000000 Binary files a/docs/demo.jpg and /dev/null differ diff --git a/docs/raspberry_tm1637.gif b/docs/raspberry_tm1637.gif new file mode 100644 index 0000000..aade18d Binary files /dev/null and b/docs/raspberry_tm1637.gif differ diff --git a/examples/clock/clock.py b/examples/clock/clock.py new file mode 100644 index 0000000..021a99c --- /dev/null +++ b/examples/clock/clock.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +from time import sleep, localtime + +from tm1637 import TM1637 + +DIO = 0 +CLK = 1 + + +class Clock: + def __init__(self, tm_instance): + self.tm = tm_instance + self.show_colon = False + + def run(self): + while True: + t = localtime() + self.show_colon = not self.show_colon + tm.numbers(t.tm_hour, t.tm_min, self.show_colon) + sleep(1) + + +if __name__ == '__main__': + tm = TM1637(CLK, DIO) + tm.brightness(1) + + clock = Clock(tm) + clock.run() diff --git a/examples/robotdyn-4-digit-decimal/test.py b/examples/robotdyn-4-digit-decimal/test.py index feb9ffa..81772a4 100644 --- a/examples/robotdyn-4-digit-decimal/test.py +++ b/examples/robotdyn-4-digit-decimal/test.py @@ -13,8 +13,11 @@ # grid5 and grid6 are not connected import tm1637 -from machine import Pin -tm = tm1637.TM1637(clk=Pin(2), dio=Pin(0)) + +CLK = 1 +DIO = 0 + +tm = tm1637.TM1637(clk=CLK, dio=DIO) # dim tm.brightness(0) diff --git a/examples/robotdyn-6-digit-decimal/test.py b/examples/robotdyn-6-digit-decimal/test.py index c841108..d2346fc 100644 --- a/examples/robotdyn-6-digit-decimal/test.py +++ b/examples/robotdyn-6-digit-decimal/test.py @@ -15,18 +15,21 @@ # this means the write function needs to shiffle the order to match import tm1637 -from machine import Pin -tm = tm1637.TM1637(clk=Pin(2), dio=Pin(0)) + +CLK = 1 +DIO = 0 + +tm = tm1637.TM1637(clk=CLK, dio=DIO) # dim tm.brightness(0) # expected: 012345 # actual: 210543 -tm.write([0x3F,0x06,0x5B,0x4F,0x66,0x6D]) +tm.write([0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D]) # data = 012345 -data = [0x3F,0x06,0x5B,0x4F,0x66,0x6D] +data = [0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D] # expected: 012345 # actual: 012345 tm.write([data[2], data[1], data[0], data[5], data[4], data[3]]) @@ -35,18 +38,20 @@ # actual: cbafed tm.show('abcdef') + # swap segments 0,2 and segments 3,5 def swap(segs): - length = len(segs) - if length == 4 or length == 5: - segs.extend(bytearray([0] * (6 - length))) - segs[0], segs[2] = segs[2], segs[0] - if length >= 4: - segs[3], segs[5] = segs[5], segs[3] - return segs + length = len(segs) + if length == 4 or length == 5: + segs.extend(bytearray([0] * (6 - length))) + segs[0], segs[2] = segs[2], segs[0] + if length >= 4: + segs[3], segs[5] = segs[5], segs[3] + return segs + # data = 012345 -data = [0x3F,0x06,0x5B,0x4F,0x66,0x6D] +data = [0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D] # expected: 012345 # actual: 012345 tm.write(swap(data)) diff --git a/examples/seeed-studio-grove-4-digit-display/test.py b/examples/seeed-studio-grove-4-digit-display/test.py index 477e12a..251d59b 100644 --- a/examples/seeed-studio-grove-4-digit-display/test.py +++ b/examples/seeed-studio-grove-4-digit-display/test.py @@ -10,8 +10,11 @@ # this module has a 4-digit 7-segment display with two dots between the 2nd and 3rd segments, referred to as the colon import tm1637 -from machine import Pin -tm = tm1637.TM1637(clk=Pin(2), dio=Pin(0)) + +CLK = 1 +DIO = 0 + +tm = tm1637.TM1637(clk=CLK, dio=DIO) # dim tm.brightness(0) diff --git a/setup.py b/setup.py index 68e87e8..e7ed3b2 100644 --- a/setup.py +++ b/setup.py @@ -1,25 +1,33 @@ import sys + +from setuptools import setup + # Remove current dir from sys.path, otherwise setuptools will peek up our # module instead of system. sys.path.pop(0) -from setuptools import setup + +with open("README.md", "r") as fh: + long_description = fh.read() setup( - name='micropython-tm1637', + name='raspberrypi-tm1637', py_modules=['tm1637'], - version='1.3.0', - description='MicroPython library for TM1637 LED driver.', - long_description='This library lets you operate quad 7-segment LED display modules based on the TM1637 LED driver.', - keywords='tm1637 seven segment led micropython', - url='https://github.com/mcauser/micropython-tm1637', + version='1.3.7', + description='Raspberry Pi Python port from MicroPython library for TM1637 LED driver.', + long_description=long_description, + long_description_content_type="text/markdown", + keywords='tm1637 raspberry pi seven segment led python', + url='https://github.com/depklyon/raspberrypi-tm1637', author='Mike Causer', author_email='mcauser@gmail.com', - maintainer='Mike Causer', - maintainer_email='mcauser@gmail.com', + maintainer='Patrick Palma', + maintainer_email='patrick.depalma@gmail.com', license='MIT', - classifiers = [ + classifiers=[ + 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', - 'Programming Language :: Python :: Implementation :: MicroPython', 'License :: OSI Approved :: MIT License', ], -) \ No newline at end of file + python_requires='>=3', + install_requires=['RPi.GPIO'] +) diff --git a/test.py b/test.py new file mode 100644 index 0000000..9d87220 --- /dev/null +++ b/test.py @@ -0,0 +1,43 @@ +import unittest + +import tm1637 + +CLK = 1 +DIO = 0 + + +class TestTm1637(unittest.TestCase): + def setUp(self): + self.tm = tm1637.TM1637(clk=CLK, dio=DIO) + + def test_encode_digit(self): + self.assertEqual(self.tm.encode_digit(0), 63) + self.assertEqual(self.tm.encode_digit(8), 127) + self.assertEqual(self.tm.encode_digit(0x0f), 113) + self.assertEqual(self.tm.encode_digit(15), 113) + # 15 or 0x0f generates a segment that can output a F character + self.assertEqual(self.tm.encode_digit(15), 113) + self.assertEqual(self.tm.encode_digit(0x0f), 113) + + def test_encode_string(self): + self.assertEqual(self.tm.encode_string(' 1'), + bytearray(b'\x00\x00\x00\x06')) + self.assertEqual(self.tm.encode_string('2 '), + bytearray(b'[\x00\x00\x00')) + self.assertEqual(self.tm.encode_string('1234'), bytearray(b'\x06[Of')) + self.assertEqual(self.tm.encode_string('-12-'), bytearray(b'@\x06[@')) + self.assertEqual(self.tm.encode_string('cafe'), bytearray(b'9wqy')) + self.assertEqual(self.tm.encode_string('CAFE'), bytearray(b'9wqy')) + self.assertEqual(self.tm.encode_string('a'), bytearray(b'w')) + self.assertEqual(self.tm.encode_string('ab'), bytearray(b'w|')) + + def test_encode_char(self): + self.assertEqual(self.tm.encode_char('1'), 6) + self.assertEqual(self.tm.encode_char('9'), 111) + self.assertEqual(self.tm.encode_char('-'), 64) + self.assertEqual(self.tm.encode_char('a'), 119) + self.assertEqual(self.tm.encode_char('F'), 113) + + +if __name__ == '__main__': + unittest.main() diff --git a/tm1637.py b/tm1637.py index a40c25d..505059b 100644 --- a/tm1637.py +++ b/tm1637.py @@ -1,9 +1,13 @@ """ +Original source for MicroPython MicroPython TM1637 quad 7-segment LED display driver https://github.com/mcauser/micropython-tm1637 +Python3 Port +https://github.com/depklyon/raspberrypi-tm1637 + MIT License -Copyright (c) 2016 Mike Causer +Copyright (c) 2016-2018 Mike Causer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -24,22 +28,29 @@ SOFTWARE. """ -from micropython import const -from machine import Pin -from time import sleep_us, sleep_ms +from time import sleep + +import RPi.GPIO as GPIO + +GPIO.setmode(GPIO.BCM) +GPIO.setwarnings(False) -TM1637_CMD1 = const(64) # 0x40 data command -TM1637_CMD2 = const(192) # 0xC0 address command -TM1637_CMD3 = const(128) # 0x80 display control command -TM1637_DSP_ON = const(8) # 0x08 display on -TM1637_DELAY = const(10) # 10us delay between clk/dio pulses -TM1637_MSB = const(128) # msb is the decimal point or the colon depending on your display +TM1637_CMD1 = 0x40 # 0x40 data command +TM1637_CMD2 = 0xc0 # 0xC0 address command +TM1637_CMD3 = 0x80 # 0x80 display control command +TM1637_DSP_ON = 0x08 # 0x08 display on +TM1637_DELAY = 0.00000001 # 10us delay between clk/dio pulses +TM1637_MSB = 0x80 # msb is the decimal point or the colon depending on your display # 0-9, a-z, blank, dash, star -_SEGMENTS = bytearray(b'\x3F\x06\x5B\x4F\x66\x6D\x7D\x07\x7F\x6F\x77\x7C\x39\x5E\x79\x71\x3D\x76\x06\x1E\x76\x38\x55\x54\x3F\x73\x67\x50\x6D\x78\x3E\x1C\x2A\x76\x6E\x5B\x00\x40\x63') +_SEGMENTS = bytearray( + b'\x3F\x06\x5B\x4F\x66\x6D\x7D\x07\x7F\x6F\x77\x7C\x39\x5E\x79\x71\x3D\x76\x06\x1E\x76\x38\x55\x54\x3F\x73\x67' + b'\x50\x6D\x78\x3E\x1C\x2A\x76\x6E\x5B\x00\x40\x63') + class TM1637(object): """Library for quad 7-segment LED modules based on the TM1637 LED driver.""" + def __init__(self, clk, dio, brightness=7): self.clk = clk self.dio = dio @@ -48,25 +59,24 @@ def __init__(self, clk, dio, brightness=7): raise ValueError("Brightness out of range") self._brightness = brightness - self.clk.init(Pin.OUT, value=0) - self.dio.init(Pin.OUT, value=0) - sleep_us(TM1637_DELAY) + GPIO.setup(self.clk, GPIO.OUT, initial=GPIO.LOW) + GPIO.setup(self.dio, GPIO.OUT, initial=GPIO.LOW) - self._write_data_cmd() - self._write_dsp_ctrl() + def __del__(self): + GPIO.cleanup(self.clk) + GPIO.cleanup(self.dio) def _start(self): - self.dio(0) - sleep_us(TM1637_DELAY) - self.clk(0) - sleep_us(TM1637_DELAY) + GPIO.output(self.clk, GPIO.HIGH) + GPIO.output(self.dio, GPIO.HIGH) + GPIO.output(self.dio, GPIO.LOW) + GPIO.output(self.clk, GPIO.LOW) def _stop(self): - self.dio(0) - sleep_us(TM1637_DELAY) - self.clk(1) - sleep_us(TM1637_DELAY) - self.dio(1) + GPIO.output(self.clk, GPIO.LOW) + GPIO.output(self.dio, GPIO.LOW) + GPIO.output(self.clk, GPIO.HIGH) + GPIO.output(self.dio, GPIO.HIGH) def _write_data_cmd(self): # automatic address increment, normal mode @@ -82,18 +92,18 @@ def _write_dsp_ctrl(self): def _write_byte(self, b): for i in range(8): - self.dio((b >> i) & 1) - sleep_us(TM1637_DELAY) - self.clk(1) - sleep_us(TM1637_DELAY) - self.clk(0) - sleep_us(TM1637_DELAY) - self.clk(0) - sleep_us(TM1637_DELAY) - self.clk(1) - sleep_us(TM1637_DELAY) - self.clk(0) - sleep_us(TM1637_DELAY) + GPIO.output(self.dio,(b >> i) & 1) + sleep(TM1637_DELAY) + GPIO.output(self.clk, GPIO.HIGH) + sleep(TM1637_DELAY) + GPIO.output(self.clk, GPIO.LOW) + sleep(TM1637_DELAY) + + GPIO.output(self.clk, GPIO.LOW) + sleep(TM1637_DELAY) + GPIO.output(self.clk, GPIO.HIGH) + sleep(TM1637_DELAY) + GPIO.output(self.clk, GPIO.LOW) def brightness(self, val=None): """Set the display brightness 0-7.""" @@ -116,17 +126,35 @@ def write(self, segments, pos=0): raise ValueError("Position out of range") self._write_data_cmd() self._start() - self._write_byte(TM1637_CMD2 | pos) for seg in segments: self._write_byte(seg) self._stop() self._write_dsp_ctrl() - def encode_digit(self, digit): + @staticmethod + def encode_digit(digit): """Convert a character 0-9, a-f to a segment.""" return _SEGMENTS[digit & 0x0f] + @staticmethod + def encode_char(char): + """Convert a character 0-9, a-z, space, dash or star to a segment.""" + o = ord(char) + if o == 32: + return _SEGMENTS[36] # space + if o == 42: + return _SEGMENTS[38] # star/degrees + if o == 45: + return _SEGMENTS[37] # dash + if 65 <= o <= 90: + return _SEGMENTS[o - 55] # uppercase A-Z + if 97 <= o <= 122: + return _SEGMENTS[o - 87] # lowercase a-z + if 48 <= o <= 57: + return _SEGMENTS[o - 48] # 0-9 + raise ValueError("Character out of range: {:d} '{:s}'".format(o, chr(o))) + def encode_string(self, string): """Convert an up to 4 character length string containing 0-9, a-z, space, dash, star to an array of segments, matching the length of the @@ -136,23 +164,6 @@ def encode_string(self, string): segments[i] = self.encode_char(string[i]) return segments - def encode_char(self, char): - """Convert a character 0-9, a-z, space, dash or star to a segment.""" - o = ord(char) - if o == 32: - return _SEGMENTS[36] # space - if o == 42: - return _SEGMENTS[38] # star/degrees - if o == 45: - return _SEGMENTS[37] # dash - if o >= 65 and o <= 90: - return _SEGMENTS[o-55] # uppercase A-Z - if o >= 97 and o <= 122: - return _SEGMENTS[o-87] # lowercase a-z - if o >= 48 and o <= 57: - return _SEGMENTS[o-48] # 0-9 - raise ValueError("Character out of range: {:d} '{:s}'".format(o, chr(o))) - def hex(self, val): """Display a hex value 0x0000 through 0xffff, right aligned.""" string = '{:04x}'.format(val & 0xffff) @@ -172,19 +183,51 @@ def numbers(self, num1, num2, colon=True): num2 = max(-9, min(num2, 99)) segments = self.encode_string('{0:0>2d}{1:0>2d}'.format(num1, num2)) if colon: - segments[1] |= 0x80 # colon on + segments[1] |= 0x80 # colon on self.write(segments) def temperature(self, num): if num < -9: - self.show('lo') # low + self.show('lo') # low elif num > 99: - self.show('hi') # high + self.show('hi') # high else: string = '{0: >2d}'.format(num) self.write(self.encode_string(string)) - self.write([_SEGMENTS[38], _SEGMENTS[12]], 2) # degrees C - + self.write([_SEGMENTS[38], _SEGMENTS[12]], 2) # degrees C + + def dec_temperature(self, num): + if num < -9.9: # limit to single digit negatives + self.write([0, 0, 0, 0]) + self.show('lo') # low + elif num > 99.9: + self.write([0, 0, 0, 0]) + self.show('hi') # high + else: + intval = abs(int(num/1)) + if num == 0: # exact zero + seg1 = 0b00000000 + seg2 = self.encode_digit(0) + seg3 = self.encode_digit(0) + else: + if intval < 10 and num > 0: # single digit positive + seg1 = 0b00000000 + seg2 = self.encode_digit(intval) + elif num < 0: # negative + seg1 = 0b01000000 # '-' + seg2 = self.encode_digit(intval) + else: # two digit (can only be positive) + seg1 = self.encode_digit(int(intval/10)) + seg2 = self.encode_digit(int(num-(int(intval/10)*10))) + + try: + seg3 = self.encode_digit(int(str(num).split('.')[1][0])) # will fail if there is no decimal point + except: + seg3 = self.encode_digit(0) + segments = [seg1, seg2, seg3, _SEGMENTS[38]] # segments and degree character + segments[1] |= 0x80 # colon as decimal + self.write(segments) + def show(self, string, colon=False): segments = self.encode_string(string) if len(segments) > 1 and colon: @@ -196,8 +239,8 @@ def scroll(self, string, delay=250): data = [0] * 8 data[4:0] = list(segments) for i in range(len(segments) + 5): - self.write(data[0+i:4+i]) - sleep_ms(delay) + self.write(data[0 + i:4 + i]) + sleep(delay / 1000) class TM1637Decimal(TM1637): @@ -213,11 +256,11 @@ def encode_string(self, string): Convert an up to 4 character length string containing 0-9, a-z, space, dash, star and '.' to an array of segments, matching the length of the source string.""" - segments = bytearray(len(string.replace('.',''))) + segments = bytearray(len(string.replace('.', ''))) j = 0 for i in range(len(string)): if string[i] == '.' and j > 0: - segments[j-1] |= TM1637_MSB + segments[j - 1] |= TM1637_MSB continue segments[j] = self.encode_char(string[i]) j += 1