Hi am hoping someone can help me. I have built a 25*8 WS2812 12v matrix display. However, I have been having issues trying to program a scrolling text display. It seems the framebuf may be the answer, though there seems to be two issues the first being frame only seems to support 16 bit colour. Not a show stopping but annoying. The next is how do map the frame buffer to my ws2812 display? Neopixel works to address and activate the leds. It is making the scrolling sign that is driving me nuts. BTW I am trying to do this in Micropython.
Hi Adrian.
This sounds fun.
Are you using any micopython libraries that we should work around?
Would you mind sharing any code you have so far?
That way we can work around the style of programming you’re used to.
Pix
Here is the code note it very much a WIP.
import machine, neopixel, time, random, sys, os
import framebuf, struct
ROWS = 8
COLS = 25
LED_PIN = 15
FRAMEBUF = COLS * ROWS
def rgbto(r,g,b):
v = (g << 16) + (r << 8) + b
return(v)
def rgbfrom(v):
b = v & 255
g = (v >> 8) & 255
r = (v >> 16) & 255
#print("V={} R={} G={} B={}".format(v,r,g,b))
return (r,g,b)
try:
msg = “Micropython Rules!”
np = neopixel.NeoPixel(machine.Pin(LED_PIN), COLS*ROWS, bpp=3)
print(id(np))
FRAMEBUF = len(msg) * ROWS * 8
fbuf = framebuf.FrameBuffer(bytearray(np), COLS, ROWS, framebuf.MONO_VLSB)
fbuf.fill(0x00)
#fbuf.hline(0,1,COLS,0x01)
#fbuf.line(1,0,COLS,1,rgbto(0,255,0))
fbuf.text(msg,0,0,0x01)
i = 0
m = 0
for r in range(ROWS):
for c in range(len(msg)8):
if fbuf.pixel(r,c) != 0:
l = l + ""
else:
l = l + " "
print(l)
l = “”
"""
while m <= FRAMEBUF:
for c in range(COLS-1,0,-1):
for r in range(0,ROWS):
rgb = fbuf.pixel(m,r)
#print("C={} R={} RGB={} Pixel={}".format(c,r,rgb,fbuf.pixel(c,r)))
if r % 2 == 0:
i = (r * COLS) + (COLS - 1) - c
else:
i = (r * COLS) + c
if rgb != 0:
np[i] = (255,0,0)
else:
np[i] = (0,0,0)
print("I={0} C={1} R={2} FRAMEBUF={3} M={4}".format(i,c,r,FRAMEBUF,m))
np.write()
time.sleep(.1)
m += 1
#fbuf.scroll(-1,0)
"""
except KeyboardInterrupt as e:
sys.print_exception(e)
print(“Exiting…”)
except IndexError as e:
sys.print_exception(e)
print(“4:Index out of range C={} R={} I={}”.format(c,r,i))
except (Exception, NameError, RuntimeError) as e:
sys.print_exception(e)
print(f"Unexpected {e=}, {type(e)=}")
finally:
np.fill((0,0,0))
np.write()
As you probably the is a lot of experimental code in there. I would like to stick with Micropython as it seem the easiest way to program the RPi Pico.
hanks Adrian.
Good news is that alllll code is work in progress. Especially my code
Tricky to read at the moment.
Can you help us out by editing your previous post and adding in some syntax to make it all pretty?
So instead of
def this_is_code(x):
if readable:
#godhelpusall
I want
def this_is_code(x):
if readable:
#much better :)
To do this you wrap the code in tick marks `
It’s the one just above your tab key on your keyboard.
Three of them in a row (optionally followed by the language you’re using).
Just like the below.
```python
def this_is_code(x):
if readable:
#These tick marks make my code so easy to understand.
```
Pix
Pix,
It looks like the copy and removed the tabs/indents. There doesn’t appear to be a attach function or am I just missing something? The upload function only seems to be for images.
Is this better. I have change the tabs to spaces
import machine, neopixel, time, random, sys, os
import framebuf, struct
ROWS = 8
COLS = 25
LED_PIN = 15
FRAMEBUF = COLS * ROWS
def rgbto(r,g,b):
v = (g << 16) + (r << 8) + b
return(v)
def rgbfrom(v):
b = v & 255
g = (v >> 8) & 255
r = (v >> 16) & 255
#print("V={} R={} G={} B={}".format(v,r,g,b))
return (r,g,b)
try:
msg = “Micropython Rules!”
np = neopixel.NeoPixel(machine.Pin(LED_PIN), COLS*ROWS, bpp=3)
print(id(np))
FRAMEBUF = len(msg) * ROWS * 8
fbuf = framebuf.FrameBuffer(bytearray(np), COLS, ROWS, framebuf.MONO_VLSB)
fbuf.fill(0x00)
#fbuf.hline(0,1,COLS,0x01)
#fbuf.line(1,0,COLS,1,rgbto(0,255,0))
fbuf.text(msg,0,0,0x01)
i = 0
m = 0
for r in range(ROWS):
for c in range(len(msg)8):
if fbuf.pixel(r,c) != 0:
l = l + ""
else:
l = l + " "
print(l)
l = “”
"""
while m <= FRAMEBUF:
for c in range(COLS-1,0,-1):
for r in range(0,ROWS):
rgb = fbuf.pixel(m,r)
#print("C={} R={} RGB={} Pixel={}".format(c,r,rgb,fbuf.pixel(c,r)))
if r % 2 == 0:
i = (r * COLS) + (COLS - 1) - c
else:
i = (r * COLS) + c
if rgb != 0:
np[i] = (255,0,0)
else:
np[i] = (0,0,0)
print("I={0} C={1} R={2} FRAMEBUF={3} M={4}".format(i,c,r,FRAMEBUF,m))
np.write()
time.sleep(.1)
m += 1
#fbuf.scroll(-1,0)
"""
except KeyboardInterrupt as e:
sys.print_exception(e)
print(“Exiting…”)
except IndexError as e:
sys.print_exception(e)
print(“4:Index out of range C={} R={} I={}”.format(c,r,i))
except (Exception, NameError, RuntimeError) as e:
sys.print_exception(e)
print(f"Unexpected {e=}, {type(e)=}")
finally:
np.fill((0,0,0))
np.write()
For some reason it won’t keep my formatting when I cut and paste
You haven’t indicated what is wrong with the mapping. For instance, can you get a single character to display? Is the sample print working? If so, why not simply replicate that code for the display?
It would simplify resolving the mapping problem if your test data was something less complex than a text string - unscrambling what is going where would be much easier with a much shorter string in some more recognizable pattern.
But the important bit which you have not mentioned is the layout of the Neopixels in the 25x8 frame - column raster, row raster, serpentine or something ese?
This what you need to do
Hi Jeff,
I am not sure how framebuf maps to a GPIO pin so I can have the contents of the frame buffer display on the LEDS.
Pix is this better?
import machine, neopixel, time, random, sys, os
import framebuf, struct
ROWS = 8
COLS = 25
LED_PIN = 15
FRAMEBUF = COLS * ROWS
def rgbto(r,g,b):
v = (g << 16) + (r << 8) + b
return(v)
def rgbfrom(v):
b = v & 255
g = (v >> 8) & 255
r = (v >> 16) & 255
#print("V={} R={} G={} B={}".format(v,r,g,b))
return (r,g,b)
try:
msg = “Micropython Rules!”
np = neopixel.NeoPixel(machine.Pin(LED_PIN), COLS*ROWS, bpp=3)
print(id(np))
FRAMEBUF = len(msg) * ROWS * 8
fbuf = framebuf.FrameBuffer(bytearray(np), COLS, ROWS, framebuf.MONO_VLSB)
fbuf.fill(0x00)
#fbuf.hline(0,1,COLS,0x01)
#fbuf.line(1,0,COLS,1,rgbto(0,255,0))
fbuf.text(msg,0,0,0x01)
i = 0
m = 0
for r in range(ROWS):
for c in range(len(msg)8):
if fbuf.pixel(r,c) != 0:
l = l + ""
else:
l = l + " "
print(l)
l = “”
"""
while m <= FRAMEBUF:
for c in range(COLS-1,0,-1):
for r in range(0,ROWS):
rgb = fbuf.pixel(m,r)
#print("C={} R={} RGB={} Pixel={}".format(c,r,rgb,fbuf.pixel(c,r)))
if r % 2 == 0:
i = (r * COLS) + (COLS - 1) - c
else:
i = (r * COLS) + c
if rgb != 0:
np[i] = (255,0,0)
else:
np[i] = (0,0,0)
print("I={0} C={1} R={2} FRAMEBUF={3} M={4}".format(i,c,r,FRAMEBUF,m))
np.write()
time.sleep(.1)
m += 1
#fbuf.scroll(-1,0)
"""
except KeyboardInterrupt as e:
sys.print_exception(e)
print(“Exiting…”)
except IndexError as e:
sys.print_exception(e)
print(“4:Index out of range C={} R={} I={}”.format(c,r,i))
except (Exception, NameError, RuntimeError) as e:
sys.print_exception(e)
print(f"Unexpected {e=}, {type(e)=}")
finally:
np.fill((0,0,0))
np.write()
Look good when I pasted it. When post it remove the leading white space I will again. Sorry.
#Micropython
import machine, neopixel, time, random, sys, os
import framebuf, struct
ROWS = 8
COLS = 25
LED_PIN = 15
FRAMEBUF = COLS * ROWS
def rgbto(r,g,b):
v = (g << 16) + (r << 8) + b
return(v)
def rgbfrom(v):
b = v & 255
g = (v >> 8) & 255
r = (v >> 16) & 255
#print("V={} R={} G={} B={}".format(v,r,g,b))
return (r,g,b)
try:
msg = "Micropython Rules!"
np = neopixel.NeoPixel(machine.Pin(LED_PIN), COLS*ROWS, bpp=3)
print(id(np))
FRAMEBUF = len(msg) * ROWS * 8
fbuf = framebuf.FrameBuffer(bytearray(np), COLS, ROWS, framebuf.MONO_VLSB)
fbuf.fill(0x00)
#fbuf.hline(0,1,COLS,0x01)
#fbuf.line(1,0,COLS,1,rgbto(0,255,0))
fbuf.text(msg,0,0,0x01)
i = 0
m = 0
for r in range(ROWS):
for c in range(len(msg)*8):
if fbuf.pixel(r,c) != 0:
l = l + "*"
else:
l = l + " "
print(l)
l = ""
"""
while m <= FRAMEBUF:
for c in range(COLS-1,0,-1):
for r in range(0,ROWS):
rgb = fbuf.pixel(m,r)
#print("C={} R={} RGB={} Pixel={}".format(c,r,rgb,fbuf.pixel(c,r)))
if r % 2 == 0:
i = (r * COLS) + (COLS - 1) - c
else:
i = (r * COLS) + c
if rgb != 0:
np[i] = (255,0,0)
else:
np[i] = (0,0,0)
print("I={0} C={1} R={2} FRAMEBUF={3} M={4}".format(i,c,r,FRAMEBUF,m))
np.write()
time.sleep(.1)
m += 1
#fbuf.scroll(-1,0)
"""
except KeyboardInterrupt as e:
sys.print_exception(e)
print("Exiting...")
except IndexError as e:
sys.print_exception(e)
print("4:Index out of range C={} R={} I={}".format(c,r,i))
except (Exception, NameError, RuntimeError) as e:
sys.print_exception(e)
print(f"Unexpected {e=}, {type(e)=}")
finally:
np.fill((0,0,0))
np.write()
That seems to have done thanks. Good tip.
The framebuff does not map to GPIO pins. The mapping you are trying to do is from a framebuff to the Neopixel sting. The first thing you need is to map each pixel in the framebuffer to a position in the Neopixel frame. It appears that this is 1-to-1, so that’s easy. Then you need to know the sequence number in the Neopixel string that corresponds to a row and column in the 25x8 matrix. You don’t have to copy them in that order, but you need to know how to calculate that sequence number from the row and column. To do that you need to know how the Neopixel string is laid out in its frame - column raster, row raster, serpentine or something else.
There are some examples of sequence numbering in a rectangular array here.
What is the result if you attempt to display just one character without scrolling?
Jeff,
Thanks for the link. I will need a bit of time to read it. If you look at my the commented out section is a very poor attempt to map the frame buffer to the Neopixel. Am I even close a solution? When I run it it doesn’t seem to work.
I have commented your code.
I have some questions you might be able to clarify
I’ve put a (??) next to them.
np = neopixel.NeoPixel(machine.Pin(LED_PIN), COLS*ROWS, bpp=3) #init display
msg = "Micropython Rules!" #Init Message
FRAMEBUF = len(msg) * ROWS * 8 #What does this do? (??)
fbuf = framebuf.FrameBuffer(bytearray(np), COLS, ROWS, framebuf.MONO_VLSB) #init buffer
fbuf.fill(0x00)#clear buffer
fbuf.text(msg,0,0,0x01) #push message into buffer
i = 0 #what is this? (??)
m = 0 #what is this? (??)
#For each pixel in the buffer .... do something to the l variable...
# Where is l declared? (??)
for r in range(ROWS):
for c in range(len(msg)*8):
if fbuf.pixel(r,c) != 0:
l = l + "*"
else:
l = l + " "
Then there is this part
"""
while m <= FRAMEBUF:
for c in range(COLS-1,0,-1):
for r in range(0,ROWS):
rgb = fbuf.pixel(m,r)
#print("C={} R={} RGB={} Pixel={}".format(c,r,rgb,fbuf.pixel(c,r)))
if r % 2 == 0:
i = (r * COLS) + (COLS - 1) - c
else:
i = (r * COLS) + c
if rgb != 0:
np[i] = (255,0,0)
else:
np[i] = (0,0,0)
print("I={0} C={1} R={2} FRAMEBUF={3} M={4}".format(i,c,r,FRAMEBUF,m))
np.write()
time.sleep(.1)
m += 1
#fbuf.scroll(-1,0)
"""
Was that meant to be a multiline string or is that meant to be code?
Did you know that in python """
means big long chuck of text?
FRAMEBUF = len(msg) * ROWS * 8 #What does this do? (??) Nothing part of the earlier experimental code to calculate the frame buffer size
fbuf = framebuf.FrameBuffer(bytearray(np), COLS, ROWS, framebuf.MONO_VLSB) #init buffer ?? Here I was trying to map the Neopixel to the frame buffer and I know it doesn't.
Though it was worth a try though.
i = 0 #what is this? (??) Experimental variables when trying to loop through the frame buffer
m = 0 #what is this? (??) look at the commented out code section
The below is just to show me that the frame buffer would scroll. Printed l out to the console. Basically you can ignore this code.
#For each pixel in the buffer .... do something to the l variable...
# Where is l declared? (??)
for r in range(ROWS):
for c in range(len(msg)*8):
if fbuf.pixel(r,c) != 0:
l = l + "*"
else:
l = l + " "
The large commented out block of code is the part where things were meant to happen. The matrix scrolling text.
That is part I am having difficulty with.
Ah gotcha.
What’s this bit about?
if r % 2 == 0:
i = (r * COLS) + (COLS - 1) - c
else:
i = (r * COLS) + c
edit for those following along, this was the serpentine logic.