Need to dynamically update text to TFT display from an ItsyB

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
xanatos
 
Posts: 110
Joined: Thu Jun 18, 2009 3:09 pm

Need to dynamically update text to TFT display from an ItsyB

Post by xanatos »

I'm pretty familiar with Python - on a Raspberry Pi processing images and speech, but I am lost when it comes to getting my ItsyBitsy M4 Express to dynamically update text to my 1.14" 240x135 pixel TFT display.

I've successfully wired up the two and am getting the "Hello World!" screen just fine.

I am going to be reading analog voltages at three pins and have those updating every 0.5 seconds (just reading one pin for now until I figure out the display part).

Here's the code I'm using and I know it's wrong because after a few dozen updates, it fails for "Memory error, memory allocation failed, allocating 512 bytes"..

Code: Select all

# Code Updated 2022-05-25 to attempt dynamic updating of voltage levels

import board
from digitalio import DigitalInOut, Direction, Pull
from analogio import AnalogOut, AnalogIn
from adafruit_display_text import label
from adafruit_st7789 import ST7789
import terminalio
import displayio
import time

# Analog input on A2
analog1in = AnalogIn(board.A2)

# Helper to convert analog input to voltage
def getVoltage(pin):
    return (pin.value * 3.3) / 65536

# First set some parameters used for shapes and text
BORDER = 5
FONTSCALE = 2
BACKGROUND_COLOR = 0x000077 # Blue
FOREGROUND_COLOR = 0x770000 # Red
TEXT_COLOR = 0xFFFF77

# Release any resources currently in use for the displays
displayio.release_displays()

spi = board.SPI()
tft_cs = board.D7
tft_dc = board.D10

display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs)
display = ST7789(display_bus, rotation=270, width=240, height=135, rowstart=40, colstart=53)

# Make the display context
splash = displayio.Group()
display.show(splash)

color_bitmap = displayio.Bitmap(display.width, display.height, 1)
color_palette = displayio.Palette(1)
color_palette[0] = BACKGROUND_COLOR

bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

while True:
    # Read analog voltage on A2
    text = "Id: %0.3f" % getVoltage(analog1in)
    text_area = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR)
    text_width = text_area.bounding_box[2] * FONTSCALE
    text_group = displayio.Group(
        scale=FONTSCALE,
        x=display.width // 2 - text_width // 2,
        y=display.height // 2 - 16,
    )
    text_group.append(text_area)
    splash.append(text_group)
    time.sleep(.5)
I'm suspecting the block after the "while True" part is completely wrong, I 'm guessing I'm appending too much to the splash and rather than just changing the text, I'm overlaying everything until I fill up available memory. I'm also guessing that what I'm trying to do is likely only going to be a single line or two of code, rather than a block for every line...

Ultimately, the display should read like this (numbers are examples only):

Id: 5.678
Vd: 2.926
Vb: 12.204

And this with the numbers updating every half second (as set by my time.sleep(.5) )

I would really appreciate some direction - Coming into the TFT display world on microcontrollers is a huge change from what I've been doing on the Raspberry Pi and I feel like a complete noob! :) Thanks!

EDIT: This seems to be working much better - it was a random shot in the dark - using display.show, rather than sprite.append:

Code: Select all

while True:
    # Read analog voltage on D0
    #print("Id: %0.3f" % getVoltage(analog1in))
    text = "Id: %0.3f" % getVoltage(analog1in)
    text_area = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR, scale=FONTSCALE)
    text_width = text_area.bounding_box[2] * FONTSCALE
    # Set the location
    text_area.x = display.width // 2 - text_width // 2
    text_area.y = display.height // 2
    display.show(text_area)
    time.sleep(.5)

User avatar
dastels
 
Posts: 15662
Joined: Tue Oct 20, 2015 3:22 pm

Re: Need to dynamically update text to TFT display from an I

Post by dastels »

Avoid creating UI elements (e.g. Label, Group) and adding to the UI structure in the main loop. Instead, create everything you need and build the UI before the loop. Inside the loop just update the contents of the UI elements.

Dave

User avatar
xanatos
 
Posts: 110
Joined: Thu Jun 18, 2009 3:09 pm

Re: Need to dynamically update text to TFT display from an I

Post by xanatos »

Hi Dave,

This sounds exactly correct and is exactly what I am looking for. However I am lost on how exactly to do it :) I have it generally working with my edit above but suspect there is a much better way to do it - as you are referencing.

Can you point me to any implementation examples?

Thanks!!

User avatar
xanatos
 
Posts: 110
Joined: Thu Jun 18, 2009 3:09 pm

Re: Need to dynamically update text to TFT display from an I

Post by xanatos »

I *think* I may have found what you're referencing: https://learn.adafruit.com/circuitpytho ... layio/text

Is this the correct place to be looking? I see things like

If you ever want to change the text of a label, you can do this:

text_area.text = "NEW TEXT"

Which would mean setting up the text area stuff up before teh while true loop, then just using the text_area.text = "NEW TEXT" line to change what's in there - is this correct thinking?

User avatar
xanatos
 
Posts: 110
Joined: Thu Jun 18, 2009 3:09 pm

Re: Need to dynamically update text to TFT display from an I

Post by xanatos »

The following is working pretty well - I think it's correct according to your direction, please advise:

Code: Select all

# Modified 2022-05-25

import board
from digitalio import DigitalInOut, Direction, Pull
from analogio import AnalogOut, AnalogIn
from adafruit_display_text import label
from adafruit_st7789 import ST7789
import terminalio
import displayio
import time

# Analog input on A2
analog1in = AnalogIn(board.A2)

# Helper to convert analog input to voltage
def getVoltage(pin):
    return (pin.value * 3.3) / 65536

# First set some parameters used for shapes and text
BORDER = 5
FONTSCALE = 3
TEXT_COLOR = 0xFFFF77

# Release any resources currently in use for the displays
displayio.release_displays()

spi = board.SPI()
tft_cs = board.D7
tft_dc = board.D10

display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs)
display = ST7789(display_bus, rotation=270, width=240, height=135, rowstart=40, colstart=53)

# Read analog voltage on A2
text = "Id: %0.3f" % getVoltage(analog1in)+"\nVd: %0.3f" % getVoltage(analog1in)+"\nVb: %0.3f" % getVoltage(analog1in)
text_area = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR, scale=FONTSCALE)
text_width = text_area.bounding_box[2] * FONTSCALE
# Set the location
text_area.x = display.width // 2 - text_width // 2
text_area.y = 9  #display.height // 2 - 40
display.show(text_area)
    
while True:
    # Read analog voltage on A2
    text_area.text = "Id: %0.3f" % getVoltage(analog1in)+"\nVd: %0.3f" % getVoltage(analog1in)+"\nVb: %0.3f" % getVoltage(analog1in)
    time.sleep(.1)

User avatar
dastels
 
Posts: 15662
Joined: Tue Oct 20, 2015 3:22 pm

Re: Need to dynamically update text to TFT display from an I

Post by dastels »

xanatos wrote:I *think* I may have found what you're referencing: https://learn.adafruit.com/circuitpytho ... layio/text

Is this the correct place to be looking? I see things like

If you ever want to change the text of a label, you can do this:

text_area.text = "NEW TEXT"

Which would mean setting up the text area stuff up before teh while true loop, then just using the text_area.text = "NEW TEXT" line to change what's in there - is this correct thinking?
Yes, that is exactly it.

Dave

User avatar
dastels
 
Posts: 15662
Joined: Tue Oct 20, 2015 3:22 pm

Re: Need to dynamically update text to TFT display from an I

Post by dastels »

xanatos wrote:The following is working pretty well - I think it's correct according to your direction, please advise:
Looks good.

Dave

User avatar
dastels
 
Posts: 15662
Joined: Tue Oct 20, 2015 3:22 pm

Re: Need to dynamically update text to TFT display from an I

Post by dastels »

The problem with recreating things like Labels is that, depending on the amount of RAM in the MCU, you risk running out of memory. Also your UI structure gets bloated with multiple unused elements, which I suspect makes updates slower.

Dave

User avatar
xanatos
 
Posts: 110
Joined: Thu Jun 18, 2009 3:09 pm

Re: Need to dynamically update text to TFT display from an I

Post by xanatos »

I've got it all working smoothly now and reintegrated with the background colors (code included below for future folks who may be starting out on these items). Thanks very much for your help!

Now I'm just working out nit-picky stuff line line spacing between text lines and scaling, which seems to require whole integers and not floats, etc...

I'm really enjoying these microcontrollers and tiny displays - very useful for many of my hand-held projects. I started out with Basic Stamps years ago, but graduated up to Raspberry Pis, but they aren't as useful in many small-scale (hand-held) projects, plus have volatility issues with not being cleanly shut down. These ATSAMD51 based controller boards have fantastic power compared to the old Basic Stamps and can do everything I need them to without the volatility of the Pi.

I expect I'll be using a lot of these once I get as proficient in their libraries and quirks as I am with the Raspberry Pi ;)

Thanks again,

Dave X.

Code for Reference:

Code: Select all

# Updated 2022-05-26

import board
from digitalio import DigitalInOut, Direction, Pull
from analogio import AnalogOut, AnalogIn
from adafruit_display_text import label
from adafruit_st7789 import ST7789
import terminalio
import displayio
import time

# Analog input on A2
analog1in = AnalogIn(board.A2)

# Helper to convert analog input to voltage
def getVoltage(pin):
    return (pin.value * 3.3) / 65536

# First set some parameters used for shapes and text
BORDER = 5
FONTSCALE = 2
BACKGROUND_COLOR = 0x000077 # Blue
FOREGROUND_COLOR = 0x770000 # Red
TEXT_COLOR = 0xFFFF77

# Release any resources currently in use for the displays
displayio.release_displays()

spi = board.SPI()
tft_cs = board.D7
tft_dc = board.D10

display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs)
display = ST7789(display_bus, rotation=270, width=240, height=135, rowstart=40, colstart=53)

# Make the display context
splash = displayio.Group()
display.show(splash)

color_bitmap = displayio.Bitmap(display.width, display.height, 1)
color_palette = displayio.Palette(1)
color_palette[0] = BACKGROUND_COLOR

bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

inner_bitmap = displayio.Bitmap(display.width - BORDER * 2, display.height - BORDER * 2, 1)
inner_palette = displayio.Palette(1)
inner_palette[0] = FOREGROUND_COLOR
inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=BORDER, y=BORDER)
splash.append(inner_sprite)

# Draw a label
text = "Initializing"
text_area = label.Label(terminalio.FONT, text=text, color=TEXT_COLOR, scale=FONTSCALE)
text_width = text_area.bounding_box[2] * FONTSCALE
text_group = displayio.Group(
    scale=1, #FONTSCALE,
    x=15, #display.width // 2 - text_width // 2,
    y=25 #display.height // 2,
)
text_group.append(text_area)
splash.append(text_group)

while True:
    # Read analog voltage on A2 for test/setup.  Will use three different pins when live
    text_area.text = (
        "Id: %0.3f" % (getVoltage(analog1in)*10) # Across 0.1 ohm resistor
        + "\nVd: %0.3f" % getVoltage(analog1in)
        + "\nVb: %0.3f" % (getVoltage(analog1in)*2) # Across voltage divider
    )
    time.sleep(0.25)

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”