luma.led_matrix icon indicating copy to clipboard operation
luma.led_matrix copied to clipboard

Support for 4-digit TM1637 display

Open thijstriemstra opened this issue 8 years ago • 10 comments

Recently got one of these 4 Bits TM1637 Digital Tube LED Clock displays. Would be cool to support this in luma.led_matrix.

thijstriemstra avatar Jan 15 '17 20:01 thijstriemstra

I've ordered one for you @rm-hull if you want to play around with it.

thijstriemstra avatar Jan 15 '17 20:01 thijstriemstra

I already encountered this while looking to buy one: raspberrytips.nl

Also found this on github

twdkeule avatar Jan 19 '17 15:01 twdkeule

Ok. It uses wiringPi2. Thanks for info though.

thijstriemstra avatar Jan 19 '17 15:01 thijstriemstra

@thijstriemstra - Package arrived today

rm-hull avatar Feb 14 '17 20:02 rm-hull

Yay nice :) @rm-hull any luck getting it to work?

thijstriemstra avatar Feb 15 '17 00:02 thijstriemstra

I connected this up, but not getting anything displayed (connected to an RPi Zero) using the scripts from https://raspberrytips.nl/tm1637-4-digit-led-display-raspberry-pi/

Not sure if it's a dud or something specific to the pi zero.

rm-hull avatar Mar 04 '17 21:03 rm-hull

Here's an arduino reference library with a wiring table: https://github.com/bremme/arduino-tm1637

Hardware setup

TM1637 PIN Arduino PIN Description
CLK Any digital pin Clock
DIO Any digital pin Digital output
VCC 5V Supply voltage
GND GND Ground

And using https://github.com/johnlr/raspberrypi-tm1637 I couldn't get it to work with CLK on gpio 21 and DIO on 20.

thijstriemstra avatar Mar 14 '17 02:03 thijstriemstra

Maybe this is easy to port.. https://github.com/blueSolder/tm1637-Chip/blob/master/tm1637_Chip.py

thijstriemstra avatar Mar 14 '17 02:03 thijstriemstra

Also couldn't get https://raspberrytips.nl/tm1637-4-digit-led-display-raspberry-pi/ to work. @twdkeule did you get it to work?

thijstriemstra avatar Mar 14 '17 02:03 thijstriemstra

any chance we can resurrect this @rm-hull? :) one of my most used projects uses this display and it feels weird to use a 'third-party' library for this, I'd love to use luma for this.

Update: I thought I was using a library but turns out it's some code I copy/pasted from somewhere, don't remember where. Anyway, here it is:

import math
import logging
import threading
from time import sleep, localtime

from ..utils import GPIO as IO


logger = logging.getLogger(__name__)

HexDigits = [0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
            0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x3D, 0x76,
            0x06, 0x1E, 0x76, 0x38, 0x55, 0x54, 0x3F, 0x73, 0x67,
            0x50, 0x6D, 0x78, 0x3E, 0x1C, 0x2A, 0x76, 0x6E, 0x5B,
            0x00, 0x40, 0x63, 0xFF]

ADDR_AUTO = 0x40
ADDR_FIXED = 0x44
STARTADDR = 0xC0
# DEBUG = False


class TM1637:
    """
    TM1637is a kind of LED (light-emitting diodedisplay) drive control special
    circuit with keyboard scan interface and it's internally integrated with
    MCU digital interface, data latch, LED high pressure drive and keyboard
    scan. Mainly used in small household electrical appliances.
    """
    __doublePoint = False
    __Clkpin = 0
    __Datapin = 0
    __brightness = 1.0  # default to max brightness
    __currentData = [0, 0, 0, 0]

    def __init__(self, CLK, DIO, brightness):
        self.__Clkpin = CLK
        self.__Datapin = DIO
        self.__brightness = brightness
        IO.setup(self.__Clkpin, IO.OUT)
        IO.setup(self.__Datapin, IO.OUT)

    def cleanup(self):
        """Stop updating clock, turn off display, and cleanup GPIO"""
        self.StopClock()
        self.Clear()
        IO.cleanup()

    def Clear(self):
        b = self.__brightness
        point = self.__doublePoint
        self.__brightness = 0
        self.__doublePoint = False
        data = [0x7F, 0x7F, 0x7F, 0x7F]
        self.Show(data)
        # Restore previous settings:
        self.__brightness = b
        self.__doublePoint = point

    def ShowInt(self, i):
        s = str(i)
        self.Clear()
        for i in range(0, len(s)):
            self.Show1(i, int(s[i]))

    def Show(self, data):
        for i in range(0, 4):
            self.__currentData[i] = data[i]

        self.start()
        self.writeByte(ADDR_AUTO)
        self.br()
        self.writeByte(STARTADDR)
        for i in range(0, 4):
            self.writeByte(self.coding(data[i]))
        self.br()
        self.writeByte(0x88 + int(self.__brightness))
        self.stop()

    def Show1(self, DigitNumber, data):
        """show one Digit (number 0...3)"""
        if(DigitNumber < 0 or DigitNumber > 3):
            return  # error

        self.__currentData[DigitNumber] = data

        self.start()
        self.writeByte(ADDR_FIXED)
        self.br()
        self.writeByte(STARTADDR | DigitNumber)
        self.writeByte(self.coding(data))
        self.br()
        self.writeByte(0x88 + int(self.__brightness))
        self.stop()

    # Scrolls any integer n (can be more than 4 digits) from right to left display.
    def ShowScroll(self, n):
        n_str = str(n)
        k = len(n_str)

        for i in range(0, k + 4):
            if (i < k):
                self.Show([int(n_str[i-3]) if i-3 >= 0 else None, int(n_str[i-2]) if i-2 >= 0 else None, int(n_str[i-1]) if i-1 >= 0 else None, int(n_str[i]) if i >= 0 else None])
            elif (i >= k):
                self.Show([int(n_str[i-3]) if (i-3 < k and i-3 >= 0) else None, int(n_str[i-2]) if (i-2 < k and i-2 >= 0) else None, int(n_str[i-1]) if (i-1 < k and i-1 >= 0) else None, None])
            sleep(1)

    def SetBrightness(self, percent):
        """Accepts percent brightness from 0 - 1"""
        max_brightness = 7.0
        brightness = math.ceil(max_brightness * percent)
        if (brightness < 0):
            brightness = 0
        if(self.__brightness != brightness):
            self.__brightness = brightness
            self.Show(self.__currentData)

    def ShowDoublepoint(self, on):
        """Show or hide double point divider"""
        if(self.__doublePoint != on):
            self.__doublePoint = on
            self.Show(self.__currentData)

    def writeByte(self, data):
        for i in range(0, 8):
            IO.output(self.__Clkpin, IO.LOW)
            if(data & 0x01):
                IO.output(self.__Datapin, IO.HIGH)
            else:
                IO.output(self.__Datapin, IO.LOW)
            data = data >> 1
            IO.output(self.__Clkpin, IO.HIGH)

        # wait for ACK
        IO.output(self.__Clkpin, IO.LOW)
        IO.output(self.__Datapin, IO.HIGH)
        IO.output(self.__Clkpin, IO.HIGH)
        IO.setup(self.__Datapin, IO.IN)

        while(IO.input(self.__Datapin)):
            sleep(0.001)
            if(IO.input(self.__Datapin)):
                IO.setup(self.__Datapin, IO.OUT)
                IO.output(self.__Datapin, IO.LOW)
                IO.setup(self.__Datapin, IO.IN)
        IO.setup(self.__Datapin, IO.OUT)

    def start(self):
        """send start signal to TM1637"""
        IO.output(self.__Clkpin, IO.HIGH)
        IO.output(self.__Datapin, IO.HIGH)
        IO.output(self.__Datapin, IO.LOW)
        IO.output(self.__Clkpin, IO.LOW)

    def stop(self):
        IO.output(self.__Clkpin, IO.LOW)
        IO.output(self.__Datapin, IO.LOW)
        IO.output(self.__Clkpin, IO.HIGH)
        IO.output(self.__Datapin, IO.HIGH)

    def br(self):
        """terse break"""
        self.stop()
        self.start()

    def coding(self, data):
        if(self.__doublePoint):
            pointData = 0x80
        else:
            pointData = 0

        if(data == 0x7F or data is None):
            data = 0
        else:
            data = HexDigits[data] + pointData
        return data

    def clock(self, military_time):
        """Clock script modified from:
            https://github.com/johnlr/raspberrypi-tm1637"""
        self.ShowDoublepoint(True)
        while (not self.__stop_event.is_set()):
            t = localtime()
            hour = t.tm_hour
            if not military_time:
                hour = 12 if (t.tm_hour % 12) == 0 else t.tm_hour % 12
            d0 = hour // 10 if hour // 10 else 36
            d1 = hour % 10
            d2 = t.tm_min // 10
            d3 = t.tm_min % 10
            digits = [d0, d1, d2, d3]
            self.Show(digits)
            # # Optional visual feedback of running alarm:
            # print digits
            # for i in tqdm(range(60 - t.tm_sec)):
            for i in range(60 - t.tm_sec):
                if (not self.__stop_event.is_set()):
                    sleep(1)

    def StartClock(self, military_time=True):
        # Stop event based on: http://stackoverflow.com/a/6524542/3219667
        self.__stop_event = threading.Event()
        self.__clock_thread = threading.Thread(
            target=self.clock, args=(military_time,))
        self.__clock_thread.start()

    def StopClock(self):
        try:
            logger.info('Attempting to stop live clock')
            self.__stop_event.set()
        except:
            logger.info('No clock to close')


class TM1637Display(object):
    """
    """
    display = None
    msg = None

    def __init__(self, clk_pin, dio_pin, brightness=1.0):
        self.clk_pin = clk_pin
        self.dio_pin = dio_pin
        self.brightness = brightness

        if IO:
            self.display = TM1637(
                CLK=self.clk_pin,
                DIO=self.dio_pin,
                brightness=self.brightness
            )
            self.clear()

    def clear(self):
        if IO:
            self.display.Clear()

    def setBrightness(self, brightness):
        if IO:
            self.display.SetBrightness(brightness)

    def write(self, msg):
        if IO:
            if msg != self.msg:
                self.msg = msg
                self.clear()
                start_pos = 0
                if len(msg) == 2:
                    start_pos = 1
                for index, ch in enumerate(msg):
                    self.display.Show1(start_pos + index, int(ch))

    def startup(self):
        if IO:
            self.setBrightness(0)
            self.write('0000')

thijstriemstra avatar Oct 18 '20 22:10 thijstriemstra