Adjust Python examples

I have an Unicorn HAT Mini, and saw that there are some nice Python examples for the Scroll HAT Mini (like the clock) which are not in the example library for the Unicorn HAT Mini. Does anybody has some tips which parts have to be changed so Scroll HAT Mini scripts run on a Unicorn HAT Mini?

Thatā€™s not likely going to be easy. The Scroll Hat Mini uses i2c while the Unicorn Mini uses SPI. They use very different matrix driver chips, and thus very different code to get things done. The Unicorn I believe does the magic with PIL.
I have a Unicorn Mini on the way, should arrive any day now. I have plans to display the time on it. If I get it working I will be happy to share my code. Might be a while though as python skills are nothing to brag about ;). And Iā€™ll likely be modifying a Unicorn example to get it done. Or doing something up from scratch.

I donā€™t have any Python skills at all. I already tried some things but no success until now, if I get sth. working I will let it know.

I currently do it on a Sense Hat, a repeating scrolling message with day, date, time, temperature etc. The Sense Hat has a scroll function built into its library. It uses an 8 x 8 RGB LED matrix.
I want color options which is why I went with the Unicorn instead of the scroll pHat.
I do believe the way to get it done is to display a string value. I havenā€™t quit sorted out how thats done though. I tried something similar with the Unicorn Hat HD, got frustrated and put it aside, meaning to get back to it. If I can get something working on the mini I should also be able to get it to work on the HD. Hopefully anyway. Thatā€™s the plan.
This may come in handy
https://www.tutorialspoint.com/python/time_strftime.htm

I donā€™t have either of these boards (yet!), but if you compare the Scroll PHAT forest fire and Unicorn Mini forest fire examples, aside from importing different modules the same difference seems to be:

# Brightness values for a tree, fire, and blank space
tree, burning, space = (0.3, 0.9, 0.0)

VS:

# Brightness values for a tree, fire, and blank space
tree = [0, 255, 0]
burning = [255, 0, 0]
space = [0, 0, 0]

So for the Scroll board an LED is given a value of 0-1 for brightness, whereas for Unicorn Mini you need three values from 0-255, one for Red, one for Green and one for Blue. At a glance, as long as you import the right module that should be all you need?

The sense hat does it (r, g, b) as does the LED Shim, and most color LED boards.
(255, 0, 0) Red
(0, 255, 0) Green
(0, 0, 255) Blue
The value can be anything from 0, off; to 255, full bright for that color. (255, 255, 0) would be yellow. There is also a brightness value the affects all the LEDā€™s as one big group.
The single color boards like the scroll hat mini will only have a brightness value, as all the LEDā€™s are one fixed color that you canā€™t change. Thatā€™s my understanding anyway, from what Hats and shims etc I have on hand.

Yeah that sounds exactly right, so theoretically moving code between the two boards is just a matter of importing the right modules and then changing whether the LED is represented by a single value of 0-1 or a triplet from 0-255.

Actually, now that you mention it, I wonder why Scroll PHAT HD takes values from 0-1, at a hardware level the LED controller chip needs a value from 0-255 so it must get converted at some point. I know that thereā€™s usually some scaling so that the brightness values change in better accordance with how the human eye percieves light but Iā€™d expect that would happen on the Unicorn Mini too.

Thanks for all the ideas, the problem I have right now, is that if I want to run the modified code for clock.py, I get following error:

Traceback (most recent call last):
File ā€œ./clock.pyā€, line 7, in
from unicornhatmini.fonts import font5x5
ModuleNotFoundError: No module named ā€˜unicornhatmini.fontsā€™

I donā€™t understand why this error comes. I copied the font5x5 from the Scroll HAT Mini repository, and copied it in my Unicorn HAT Mini repository, with exactly the same folderstructure. First I thought it might be some problem with the file permissions, so I set all folders and files to 777, but the error still shows up.
So is there a trick with importing modules or files? Because after this error is gone there will come more for sure, and they probably demand more knowledge than I have.

Can you post the first 10 lines of the script?

Inside the unicornhatmini folder should be a file called __init__.py and then the fonts folder.

Alternatively, I think you should be able to paste the font into the same directory as your script and just import font5x5.

1 Like

@Shoe Regarding the 0-255 color value versus 0-1 for brightness. I never got that? The brightness part anyway? The Sense Hat just has a low_light python option, that is half bright. To do anything else you have to adjust all the (r, g, b) vales. PITA
I get the 0-255 part, 255 is 11111111 in binary, an 8 bit register.
And you need the finite values to get specific colors like orange for example, (255, 140, 0).
I guess if all you have is ā€œoneā€ color, simplifying the value makes sense?
Since you can go decimal points 0.1, 0.2 etc, its like going from 0 to 10.

I should probably mention that I am partially color blind. I have red green color deficiency. Itā€™s a bit hard to wrap your head around, but I can see red and green. Turquois is a problem for me, I either see blue or green, lol. As far as RGB LEDā€™s go I can have a problem determining if its green or yellow in certain light levels. In bright light I canā€™t read Red or Blue text very well.
To get around this I have my python code turn all text bright white in bright sunlight conditions. Colored text at full bright in a Sunlit area, and color text dimmed to half bright in dark nighttime conditions. itā€™s what works for me. I have an ambient light sensor on my portable weather clock so its all dome automatically.

Youā€™re absolutely right, but at a hardware level the brightness is stored as 8 bit values, 3 for RGB or just one for a Scroll Phat board which just has brightness. Thatā€™s why I wonder why the official library takes values from 0-1, at some point it will have to convert those to 0-255 anyway and taking a 0-255 value would keep it consistent with other LED modules.

Hardly a big deal, Iā€™m just curious.

These are the first lines of the script

#!/usr/bin/env python3

import time

from colorsys import hsv_to_rgb
from unicornhatmini import UnicornHATMini
from unicornhatmini.fonts import font5x5

print(ā€œā€"
Unicorn HAT Mini: Clock
Displays hours and minutes in text,
plus a seconds progress bar.
Press Ctrl+C to exit!
ā€œā€")

The init.py and the fonts folder are both in the unicornhatmini folder, but I pasted the font in the same directory and now I donā€™t get this error anymore, so no idea what went wrong. Now itā€™s time to move on to the next error :)

It was only a matter of time, here is the next error which I canā€™t solve:

Traceback (most recent call last):
  File "./clock.py", line 30, in <module>
    unicornhatmini.clear()
NameError: name 'unicornhatmini' is not defined

Below is the whole script

#!/usr/bin/env python3

import time
import font5x5

from colorsys import hsv_to_rgb
from unicornhatmini import UnicornHATMini

print("""
Unicorn HAT Mini: Clock
Displays hours and minutes in text,
plus a seconds progress bar.
Press Ctrl+C to exit!
""")

# Display a progress bar for seconds
# Displays a dot if False
DISPLAY_BAR = False

# Brightness of the seconds bar and text
BRIGHTNESS = 0.3

# Uncomment the below if your display is upside down
#   (e.g. if you're using it in a Pimoroni Scroll Bot)
# unicornhatmini.rotate(degrees=180)

while True:
    unicornhatmini.clear()

    # Grab the "seconds" component of the current time
    # and convert it to a range from 0.0 to 1.0
    float_sec = (time.time() % 60) / 59.0

    # Multiply our range by 15 to spread the current
    # number of seconds over 15 pixels.
    #
    # 60 is evenly divisible by 15, so that
    # each fully lit pixel represents 4 seconds.
    #
    # For example this is 28 seconds:
    # [x][x][x][x][x][x][x][ ][ ][ ][ ][ ][ ][ ][ ]
    #  ^ - 0 seconds                59 seconds - ^
    seconds_progress = float_sec * 15

    if DISPLAY_BAR:
        # Step through 15 pixels to draw the seconds bar
        for y in range(15):
            # For each pixel, we figure out its brightness by
            # seeing how much of "seconds_progress" is left to draw
            # If it's greater than 1 (full brightness) then we just display 1.
            current_pixel = min(seconds_progress, 1)

            # Multiply the pixel brightness (0.0 to 1.0) by our global brightness value
            unicornhatmini.set_pixel(y + 1, 6, current_pixel * BRIGHTNESS)

            # Subtract 1 now we've drawn that pixel
            seconds_progress -= 1

            # If we reach or pass 0, there are no more pixels left to draw
            if seconds_progress <= 0:
                break

    else:
        # Just display a simple dot
        unicornhatmini.set_pixel(int(seconds_progress), 6, BRIGHTNESS)

    # Display the time (HH:MM) in a 5x5 pixel font
    unicornhatmini.write_string(
        time.strftime("%H:%M"),
        x=0,                   # Align to the left of the buffer
        y=0,                   # Align to the top of the buffer
        font=font5x5,          # Use the font5x5 font we imported above
        brightness=BRIGHTNESS  # Use our global brightness value
    )

    # int(time.time()) % 2 will tick between 0 and 1 every second.
    # We can use this fact to clear the ":" and cause it to blink on/off
    # every other second, like a digital clock.
    # To do this we clear a rectangle 8 pixels along, 0 down,
    # that's 1 pixel wide and 5 pixels tall.
    if int(time.time()) % 2 == 0:
        unicornhatmini.clear_rect(8, 0, 1, 5)

    # Display our time and sleep a bit. Using 1 second in time.sleep
    # is not recommended, since you might get quite far out of phase
    # with the passing of real wall-time seconds and it'll look weird!
    #
    # 1/10th of a second is accurate enough for a simple clock though :D
    unicornhatmini.show()
    time.sleep(0.1)

So line 30 in my script is:

> unicornhatmini.clear()

And the same line in the original (Scroll HAT Mini) is:

> scrollphathd.clear()

To be sure I even copied the __init__.py in the same folder as the script, but the error is still there. So I checked both __init__.py files (from the Unicorn HAT Mini and Scroll HAT mini), and they both have a part which says def.clear. I replaced the def.clear from the Unicorn HAT Mini itā€™s __init__.py whicht was:

def clear(self):
    """Set all pixels to 0."""
    self.set_all(0, 0, 0)

to (which is from the Scroll HAT Mini its __init__.py):

def clear():
    """Clear the buffer.
    You must call `show` after clearing the buffer to update the display.
    """
    global _current_frame, _scroll, buf

    _current_frame = 0
    _scroll = [0, 0]

    buf = numpy.zeros((DISPLAY_WIDTH, DISPLAY_HEIGHT))

And even after this I get the same error. Any ideas what it could be? I really hope I made this post clear and not too long, just wanted to be detailed enough.

I think you need the following line early in your file. Above the while true.
unicornhatmini = UnicornHATMini()

EDIT: Iā€™m still waiting for mine to arrive. =(

1 Like

I know how horrible it is to wait for gadgets, letā€™s hope yours arrives on monday!
And here is already the next error:

Traceback (most recent call last):
  File "./clock.py", line 68, in <module>
    unicornhatmini.set_pixel(int(seconds_progress), 6, BRIGHTNESS)
TypeError: set_pixel() missing 2 required positional arguments: 'g' and 'b'

By now I guess that probably all errors which will be coming now are because the __init__.py of both HATā€™s are different in the way that each function (is it called like that?) has another script, which makes sense because the Unicorn HAT mini has RGB pixels/LEDā€™s and the Scroll HAT mini only one color pixels/LEDā€™s, and because the Unicorn HAT Mini has a Python 3 script and the Scroll HAT Mini has a Python script, please correct me if Iā€™m wrong (probably Iā€™ll find out by myself).

For the Scroll HAT Mini the set_pixel is:

def set_pixel(x, y, brightness):
    """Set a single pixel in the buffer.
    param x: Position of pixel from left of buffer
    param y: Position of pixel from top of buffer
    param brightness: Intensity of the pixel, from 0.0 to 1.0

And for the Unicorn HAT Mini it is:

def set_pixel(self, x, y, r, g, b):
    """Set a single pixel."""
    offset = (x * _ROWS) + y
    if self._rotation == 90:
        y = _COLS - 1 - y
        offset = (y * _ROWS) + x
    if self._rotation == 180:
        x = _COLS - 1 - x
        y = _ROWS - 1 - y
        offset = (x * _ROWS) + y
    if self._rotation == 270:
        x = _ROWS - 1 - x
        offset = (y * _ROWS) + x
    self.disp[offset] = [r >> 2, g >> 2, b >> 2]

Line 68 in the script is:

scrollphathd.set_pixel(int(seconds_progress), 6, BRIGHTNESS)

What I did was to delete the 6, and put 1,1,1, instead, which left me with the next error:

Traceback (most recent call last):
  File "./clock.py", line 68, in <module>
    unicornhatmini.set_pixel(int(seconds_progress), 3,3,3, BRIGHTNESS)
  File "/usr/local/lib/python3.7/dist-packages/unicornhatmini/__init__.py", line 97, in set_pixel
    self.disp[offset] = [r >> 2, g >> 2, b >> 2]
TypeError: unsupported operand type(s) for >>: 'float' and 'int'

So I put 3,3,3, instead of 1,1,1, because of the self.disp[offset] = [r >> 2, g >> 2, b >> 2], but the same error popped up again, so my guess is that it is about the TypeError: unsupported operand type(s) for >>: 'float' and 'int'. And before I mess up the script completely I ask again for help.

So much to learn, but I understand a bit more after each error or question here and thatā€™s a good process :)

The scroll mini only needs 3 values / argument to set a pixel.
The row x, the column y, and the brightness.

The Unicorn Mini needs more arguments / values
If your setting ā€œaā€ specific pixel its needs the row number x, column number y, and the color info, the 0-255 values for r, g, b.

3 versus 5 which is why you got the 2 missing error. ;)
You need to swap out the brightness value for the three r, g, b, values.

You may also find they each reference x and y differently, you wonā€™t know until you get one to light up and see which one lights up.

I would think any reference to scrollphathd will have to replaced with unicornhatmini. And other code adjustments to make it compatible.

And using (1, 1, 1) for the r, g, b isnā€™t going to work. The led likely wonā€™t be bright enough to see it. On my sense hat anything below about 50 and the LED isnā€™t lit up enough to see it. Try 100, 100, 100 or 255, 255, 255. The latter will get you a nice bright white led.

The scroll hat mini has A 17x7 matrix of bright white LEDs with individual brightness control. You are likely going to have to strip out the ā€œBrightnessā€ references in your code.
For the Unicorn Hat Mini its a set it for all situation, and its set in the section above the ā€œwhile trueā€
Iā€™m thinking the Brightness has to be removed and replaced with the r, g, b values. Something like that?

I already changed the references to unicornhatmini, and I changed the values, and deleted the Brightness reference. Now itā€™s this one:

Traceback (most recent call last):
  File "./clock.py", line 65, in <module>
    unicornhatmini.set_pixel(int(seconds_progress), x,y,100,100,100)
NameError: name 'x' is not defined

The brightness section is set to 0.3, which is ok because some other examples also use it, and doesnā€™t need the RGB values, but good thought anyways.