Interstate 75 with multiple HUB-75 LED matrix panels

Anybody here using an Interstate 75 or 75W with multiple Hub 75 LED Matrix panels? Specifically vertically one under the other not side by side?

I have two 64 wide by 32 high panels setup for a 64 x 64 LED Matrix. It’s doable on an Adafruit Matrix Portal as it supports chain across, tile down and serpentine. There was no Interstate 75 W when I bought it, and i wanted WIFI. Adafruit’s displayio has been a struggle so I had a rethink and ordered an Interstate 75 W.

Should have read the fine print though as it appears it doesn’t support stacking vertically?
Getting Started with Interstate 75 (pimoroni.com)
Note that if you’re using MicroPython/PicoGraphics it only currently supports chained panels arranged next to each other in a line.

My own fault for not researching fully. Just wondering if things have changed? Or if anybody has found a work around?
@hel @Matt

Naïve question: is it possible to physically rotate the panels 90º (and subsequently rotate the image or whatever you’re trying to display using code?)

What does your project involve? You can use the hub75 module with panels arranged in a grid, just not the fancy new PicoGraphics stuff, so if you’re just setting individual pixels you’re fine?

As I understand it we had some trouble figuring out rotation/translation for implementing a grid layout in PicoGraphics in time for release - it’s definitely possible that this could be made to work in the future but it would require some developer time.

A possible workaround (depending on what your project involves!) might be to tell PicoGraphics that your panels were arranged in a line and just adjust the position of where you wanted to draw things to allow for the panels being in two rows. I think I’d need to draw that out on paper to help my brain figure out what would need to go where though :)

Ah, time to have a look at the hub 75 module. I want to scroll text.

Is there some detailed info on using the HUB 75 module? I’m not finding much?
pimoroni-pico/micropython/modules/hub75 at main · pimoroni/pimoroni-pico (github.com)

Not much here either?
Getting Started with Interstate 75 (pimoroni.com)

What kind of thing are you looking for - how to use chained panels? I think if you’re using the raw hub75 library you should just be able to set the WIDTH and HEIGHT to the correct dimensions for your combined matrix.

Having slept on it - it seems like it would be pretty easy to use a long thin PicoGraphics buffer to scroll text on a 2x2 panel display - you’d just need to pass your scroll function a fake WIDTH value so it cut off after the first two panels.

There’s a Galactic Unicorn scrolling text example here that probably wouldn’t be difficult to port: pimoroni-pico/scrolling_text.py at main · pimoroni/pimoroni-pico · GitHub

Keep in mind that I don’t have the Interstate 75 W yet. It’s ordered by hasn’t shipped yet. I want to read up on how to proceed once I get it. ;) I was going to also get a 64 by 64 panel, but they were out of stock.

My two panels are 64 wide by 32 high.
And are chained together data out to data in.
Neither one is flipped and one is below the other for a combined 64 by 64.
RGB LED Matrix Panel - 32x64 4mm pitch - Pimoroni

On my Matrix Portal it configures like this.

base_width = 64     (width of individual panel)
base_height = 32    (height of individual panel)
chain_across = 1    (not chained across)
tile_down = 2       (2 panels tiled down) 
serpentine = False  (no panels flipped / turned) 

That sets it to 64 by (32 x 2). I have 1 or 2 basic Adafruit Circuit Python examples running. What’s killing me on the Circuit Python side is having to learn Display IO. I’m really getting into Pico Graphics these days, and learning more and more. The learning curve doesn’t seem near so steep.
The other gotcha, is some examples in the Adafruit Tutorials are written for different display driver boards. I’m left with “if this isn’t the board your using edit the appropriate section”. And no clear (not to me anyway) instructions on what to change? Rant over.

Anyway, that’s where I’m at. If I can make Micro Python “think” its one 64 x 64 panel, that’s one big hurdle over. I’ll try 64 by 64 when I get it and see what happens.

In the end I hope to display scrolling weather info messages, “It’s -10c outside” etc. With the amount of info I want to put across a static display isn’t going to work. Not without adding even more panels, lol.

1 Like

Ok, just got my Interstate 75 W.
128x32 works just fine with the balls demo, with them end to end.
64x64 doesn’t though, not even close. Hard to describe what I see but they are split, and duplicated.

Can you point me to some info on how to do that, please?
If I use anything except

WIDTH = 128
HEIGHT = 32

Nothing is where its supposed to be?

Can you post a photo? And your code?

I’ll post some pics and code to this post when I get a chance. I have it all in pieces at the moment. I’m making a stand for it.

For testing I’m using this code.

import time
import hub75

WIDTH = 128
HEIGHT = 32

matrix = hub75.Hub75(WIDTH, HEIGHT)

matrix.start()
# X, Y, R, G, B
matrix.set_pixel(127, 31, 0, 0, 255)

time.sleep(10.0)
matrix.clear()

I just change the X and Y position data to see what pixel lights up.
With
WIDTH = 128
HEIGHT = 32
The correct pixel lights up.
With
WIDTH = 64
HEIGHT = 64
And corresponding X, Y values, nothing greater than 63, the position is wrong. Either not there at all or in the wrong spot.

I have my two panels setup physical 64 x 64 layout.
One 64 W x 32 H above a second 64 W x 32 H.
Interstate 75 is plugged into the lower panel.

With Width set to 128 and Height set to 32 it ends up like this
0, 0-------63, 0
Top Panel
0, 31----63, 31

64, 0-----127, 0
Bottom Panel
64, 31—127, 31

With Width set to 64 and Height set to 64 it ends up like this
?, ?-------?, ?
Top Panel
?, ?----63, 0

0, 0-----?, ?
Bottom Panel
0, 63—63, 63

63, 63 lights up two pixels? Bottom Right of the bottom panel and 16, 30 on the top panel?

Running this;

from interstate75 import Interstate75

i75 = Interstate75(display=Interstate75.DISPLAY_INTERSTATE75_128X32)
graphics = i75.display

MAGENTA = graphics.create_pen(255, 0, 255)
BLACK = graphics.create_pen(0, 0, 0)
WHITE = graphics.create_pen(255, 255, 255)

graphics.set_pen(BLACK)
graphics.clear()

graphics.set_pen(MAGENTA)
graphics.text("hello", 1, 10, scale=2)
graphics.set_pen(WHITE)
graphics.text("world", 65, 10, scale=2)
i75.update(graphics)

Gets me

HELLO (top display)
WORLD (bottom display

Compromises were made, no choice really with the lack of info on how to get things done. It’s claimed you can scroll text on it, but I have yet to even find a hint as to how to do that. I did have a go at modifying an example for the Galactic Unicorn but could not get it to work. That’s mostly due to my lack of skill in Micro Python. Still IMHO it shouldn’t be this hard?
I have my panels the way I wanted them, and can display the minimal basic info I want to.
Time
Temperature
Humidity
Pressure

It still thinks they are 128 x 32 so I had to compensate in my code to get things where I wanted on screen.

import time
import machine
from interstate75 import Interstate75
from interstate75 import Interstate75, SWITCH_A, SWITCH_B
i75 = Interstate75(display=Interstate75.DISPLAY_INTERSTATE75_128X32)

graphics = i75.display
graphics.set_font("bitmap8")
#graphics.set_font("8x12")
#graphics.set_font("10x14")

t_color = graphics.create_pen(0,0,0)
h_color = graphics.create_pen(0,0,0)
p_color = graphics.create_pen(0,0,0)

black = graphics.create_pen(0,0,0)
red = graphics.create_pen(255,0,0)
green = graphics.create_pen(0,255,0)
blue = graphics.create_pen(0,0,255)
yellow = graphics.create_pen(255,255,0)
orange = graphics.create_pen(255,140,0)
white = graphics.create_pen(255,255,255)

graphics.set_pen(black)
graphics.clear()
i75.update(graphics)
i75.set_led(0, 0 ,0)

from breakout_bme280 import BreakoutBME280
from pimoroni_i2c import PimoroniI2C

i2c = PimoroniI2C(sda=(20), scl=(21))
bme = BreakoutBME280(i2c, 0x76)
temperature, pressure, humidity = bme.read()

from breakout_rtc import BreakoutRTC
from machine import RTC
RV3028 = BreakoutRTC(i2c)

rtc = BreakoutRTC(i2c)
if rtc.is_12_hour:
    rtc.set_24_hour()
    
RV3028.update_time()
hour = rtc.get_hours()
minute = rtc.get_minutes()
month = rtc.get_month()
date = rtc.get_date()    
     
if rtc.read_periodic_update_interrupt_flag():
    rtc.clear_periodic_update_interrupt_flag()

if rtc.update_time():
    rtc_date = rtc.string_date()
    rtc_time = rtc.string_time()    


temperature, pressure, humidity = bme.read()
time.sleep (0.5)

start_time = time.time()

while True:
    graphics.set_pen(black)
    graphics.clear()
    i75.update(graphics)
    
    time_elapsed = time.time() - start_time
    RV3028.update_time()
    hour = rtc.get_hours()
    minute = rtc.get_minutes()
    month = rtc.get_month()
    date = rtc.get_date()    
     
    if rtc.read_periodic_update_interrupt_flag():
        rtc.clear_periodic_update_interrupt_flag()

    if rtc.update_time():
        rtc_date = rtc.string_date()
        rtc_time = rtc.string_time()
    #year, month, day, wd, hour, minute, second, _ = rtc.datetime()    
    
    graphics.set_pen(white)
   
    if hour == 0:
        graphics.text(f"{12}:{minute:02}AM", 1, 1, scale=2)    
    elif 0 <= hour < 10:
        graphics.text(f"{hour:1}:{minute:02}AM", 7, 1, scale=2)
    elif 10 <= hour < 12:
        graphics.text(f"{hour:2}:{minute:02}AM", 1, 1, scale=2)
    elif hour == 12:
        graphics.text(f"{hour:2}:{minute:02}PM", 1, 1, scale=2)    
    elif hour > 12:
        hour = hour - 12
        if hour < 10:
            graphics.text(f"{hour:1}:{minute:02}PM", 7, 1, scale=2)
        elif 10 <= hour < 12:
            graphics.text(f"{hour:2}:{minute:02}PM", 1, 1, scale=2)
        elif hour == 12:
            graphics.text(f"{hour:2}:{minute:02}AM", 1, 1, scale=2)        
        
    temperature, pressure, humidity = bme.read()
    
    temperature = round(temperature)

    if temperature < -10:
        t_color = white
    elif -10 <= temperature <= 0:
        t_color = blue
    elif 0 < temperature <= 12:
        t_color = yellow
    elif 12 < temperature <= 16:
        t_color = green
    elif 16 < temperature <= 24:
        t_color = green      
    elif 24 < temperature <= 27:
        t_color = orange
    elif temperature > 27:
        t_color = red

    graphics.set_pen(t_color)
    graphics.text("{:0.0f}°C" .format(temperature), 15, 17, scale=2)
    
    humidity = round(humidity)   

    if humidity < 30:
        h_color = red
    elif 30 <= humidity <= 60:
        h_color = green
    elif 60 < humidity < 80:
        h_color = yellow
    elif humidity >= 80:
        h_color = orange        

    graphics.set_pen(h_color)
    graphics.text("{:0.0f}%".format(humidity), 79, 1, scale=2)

    pressuremb = pressure / 100           
    pressuremb = round(pressuremb)
    
    if pressuremb < 982:
        p_color = red
    elif 982 <= pressuremb < 1004:
        p_color = yellow
    elif 1004 <= pressuremb < 1026:
        p_color = green
    elif 1026 <= pressuremb < 1048:
        p_color = blue
    elif pressuremb >= 1048:
        p_color = orange
 
    graphics.set_pen(p_color)
    graphics.text("{:0.0f}mb" .format(pressuremb), 65, 17, scale=2)

    i75.update(graphics)
    time.sleep (30)

I think I got the Galactic scrolling text example working on Interstate without too much trouble, though not with your complicated panel layout admittedly. I’ll try and dig out the code when I get a moment (it’s been a manic week so far!)

Yeah, its almost like something Cosmic is going to happen. ;)

EDIT: Actually, it looks like the new Inky stuff has been what is keeping you busy.

1 Like

Here is a pic of the backside, with the wiring / mounting.

I modified the included power cable to power both panels and the i75, and have a plug to plug into the power supply. I used this as the plug for the power supply.
T Connector Male-Female Pair (pimoroni.com)
5V 4A switching power supply.
The front side looks like this. The colors washed out trying to get the detail so I fiddled around and took two pics.


Too prevent power issues from two +5V sources, I modified a USB cable so its Data only. The +5V wire was cut. Everything has the one common ground, and the display / i75 is all powered by the one dedicated power supply.

I had another go at scrolling text and got it to work this time.
It ends up like this but it works.

Space is big. Really bi
g. You just won’t believ

Anybody know what I change to make the text bigger? scale = 2

#import time
#from galactic import GalacticUnicorn
#from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY

import time
from interstate75 import Interstate75
from interstate75 import Interstate75, SWITCH_A, SWITCH_B



# constants for controlling scrolling text
PADDING = 5
MESSAGE_COLOUR = (255, 255, 255)
OUTLINE_COLOUR = (0, 0, 0)
BACKGROUND_COLOUR = (10, 0, 96)
MESSAGE = "\"Space is big. Really big. You just won't believe how vastly hugely mind-bogglingly big it is. I mean, you may think it's a long way down the road to the chemist, but that's just peanuts to space.\" - Douglas Adams"
HOLD_TIME = 2.0
STEP_TIME = 0.075

# create galactic object and graphics surface for drawing
#gu = GalacticUnicorn()
#graphics = PicoGraphics(DISPLAY)

i75 = Interstate75(display=Interstate75.DISPLAY_INTERSTATE75_128X32, stb_invert=True)
graphics = i75.display

#width = GalacticUnicorn.WIDTH
#height = GalacticUnicorn.HEIGHT

width = 128
height = 32


# function for drawing outlined text
def outline_text(text, x, y):
    graphics.set_pen(graphics.create_pen(int(OUTLINE_COLOUR[0]), int(OUTLINE_COLOUR[1]), int(OUTLINE_COLOUR[2])))
    graphics.text(text, x - 1, y - 1, -1, 1)
    graphics.text(text, x, y - 1, -1, 1)
    graphics.text(text, x + 1, y - 1, -1, 1)
    graphics.text(text, x - 1, y, -1, 1)
    graphics.text(text, x + 1, y, -1, 1)
    graphics.text(text, x - 1, y + 1, -1, 1)
    graphics.text(text, x, y + 1, -1, 1)
    graphics.text(text, x + 1, y + 1, -1, 1)

    graphics.set_pen(graphics.create_pen(int(MESSAGE_COLOUR[0]), int(MESSAGE_COLOUR[1]), int(MESSAGE_COLOUR[2])))
    graphics.text(text, x, y, -1, 1)


#gu.set_brightness(0.5)

# state constants
STATE_PRE_SCROLL = 0
STATE_SCROLLING = 1
STATE_POST_SCROLL = 2

shift = 0
state = STATE_PRE_SCROLL

# set the font
graphics.set_font("bitmap8")

# calculate the message width so scrolling can happen
msg_width = graphics.measure_text(MESSAGE, 1)

last_time = time.ticks_ms()

while True:
    time_ms = time.ticks_ms()

    #if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
        #gu.adjust_brightness(+0.01)

    #if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
        #gu.adjust_brightness(-0.01)

    if state == STATE_PRE_SCROLL and time_ms - last_time > HOLD_TIME * 1000:
        if msg_width + PADDING * 2 >= width:
            state = STATE_SCROLLING
        last_time = time_ms

    if state == STATE_SCROLLING and time_ms - last_time > STEP_TIME * 1000:
        shift += 1
        if shift >= (msg_width + PADDING * 2) - width - 1:
            state = STATE_POST_SCROLL
        last_time = time_ms

    if state == STATE_POST_SCROLL and time_ms - last_time > HOLD_TIME * 1000:
        state = STATE_PRE_SCROLL
        shift = 0
        last_time = time_ms

    graphics.set_pen(graphics.create_pen(int(BACKGROUND_COLOUR[0]), int(BACKGROUND_COLOUR[1]), int(BACKGROUND_COLOUR[2])))
    graphics.clear()

    outline_text(MESSAGE, x=PADDING - shift, y=2)

    # update the display
    #gu.update(graphics)
    i75.update(graphics)
    # pause for a moment (important or the USB serial device will fail)
    time.sleep(0.001)

Never mind figured that out too.

def outline_text(text, x, y):
    graphics.set_pen(graphics.create_pen(int(OUTLINE_COLOUR[0]), int(OUTLINE_COLOUR[1]), int(OUTLINE_COLOUR[2])))
    graphics.text(text, x - 1, y - 1, -1, 2)
    graphics.text(text, x, y - 1, -1, 2)
    graphics.text(text, x + 1, y - 1, -1, 2)
    graphics.text(text, x - 1, y, -1, 2)
    graphics.text(text, x + 1, y, -1, 2)
    graphics.text(text, x - 1, y + 1, -1, 2)
    graphics.text(text, x, y + 1, -1, 2)
    graphics.text(text, x + 1, y + 1, -1, 2)

    graphics.set_pen(graphics.create_pen(int(MESSAGE_COLOUR[0]), int(MESSAGE_COLOUR[1]), int(MESSAGE_COLOUR[2])))
    graphics.text(text, x, y, -1, 2)