Hi, members, I just published this repo
It uses a Pimoroni Presto as a subscriber device for MQTT messages.
The Presto is programmed, using a micropython script, to subscribe to four different topics.
As MQTT Publisher device I use an Adafruit Feather ESP32-S3 TFT board with three external I2C devices connected (a RTC unit, a Pimoroni multi-sensor-stick and an Adafruit Gamepad QT).
As MQTT Broker device I use a Raspberry Pi Compute Module 5.
In this setup the seven ambient light leds of the Presto can be remotely switched On or Off.
The color of these ambient leds can also remotely be changed. The remote commands are done using the buttons of the Gamepad .
Default the Publisher device will send, every minute, sensor topic MQTT messages, containing readouts from the connected BME280 sensor on the multi-sensor-stick. The external RTC will be synchronized every 15 minutes from a NTP unix timestamp.
Good stuff paulsk. Thanks for sharing the code and the detailed writeup!
Hey Bucaneers! I updated my repo with a version 2. Now able to send MQTT messages to change the text color of the Presto remotely. Now using Pimoroni QwST Pad I2C game controllers.
I just finished creating a MicroPython script for my new Pimoroni Pico LiPo 2XL W to do the following:
-
connect via WiFi;
-
get, at intervals, a NTP unixTime datetime stamp;
-
get, at other intervals, a weather message from metar-taf.com; filter from that the METAR section of my local airport (Lisbon, Portugal, LPPT);
-
forward the METAR data as payload of a MQTT message to a local MQTT Broker.
Note I started to use the Pico LiPo 2XL W instead of an Arduino Uno R4 W because the Uno lacked the needed memory space.
Next I will try to use both Adafruit Feather ESP32-S3 TFT as MQTT Publisher for TPAH data and color change commands to the Pimoroni Presto as announced in this post and also use at the same time the Pimoroni Pico LiPo 2XL W as a MQTT Publisher for METAR data. Let’s see how the Presto can coop with both. I must say that, until now, I am very satisfied, even astonished about the capabilities (memory wise) of the Presto. The Micropython script for the Presto has already 2722 lines of code and commentary lines.
I will publish the code soon on Github.
See an image I just took: photo
Update: just finished the test using 2 MQTT Publishers and 1 MQTT Subscriber.
Created a short video
For those interested, I created (and updated today) a repo using an Unexpected Maker SQUiXL device as MQTT Subscriber. At the end of the README.md I added a link to photos in which a photo of both a Pimoroni Presto and an Unexpected Maker SQUiXL are shown while displaying the same METAR weather data received by MQTT message. See repo SQUiXL
Its good to see some code examples and yours look to be excellent.
I also have both the squixl and the presto and I had a peek at the photos. I remark that the font and resolution thats displayed on the squixl could have also been used on the presto (in high res mode), so I hope anyone does not take the ‘blocky’ text on the presto to indicate the quality of the screen. Screenwise, the results on both are can be very good quality.
The squixl is probably the better product as it has a proper speaker, a printed case, battery (I cannot hear the piezo thing on the presto at all) but it is twice the price taking shipping and docking stand into account compared to getting the presto in the last sale.
I see your squixl example is in C++ . It’s probably also worth mentioning that similar micropython code can be made to run on both, with minor differences in the screen set up code and I have examples of running very similar programs (using touch and multiple screens etc) on both and screenwise in quality and touch response they are quite identical.
@SirFico Thank you for your reply!. I did not show both devices beside eachother to compare the resolution or quality of the fonts. Only to show the content.
I would like to experiment with less “blocky” fonts on the Presto. I like the Presto very much!
The SQUiXL example is the SQUiXL-DevOS firmware that I just adepted for my MQTT trials.
@paulsk if you want a handy example of Presto high res fonts then have a peep at the Presto with Google sheets topic a few posts down in this forum where I posted a ‘noddy’ example when someone was asking how to get text on buttons. This was using the Presto vector lib.
Alternatively you may like to look at the micropython squixl example I posted in the um squixl bit of his forum, in the post just before your post on mqtt posted yesterday.
When the squixl came out UM Seon, for the micropython examples, only had a demo of using the minuscule mp framefuffer font. I put up an example of using the Peter Hinch font-to-py fonts which he then adopted for his example mp code. If you want to use these same font routines with the Presto then I have examples I can share, and indeed its my preferred way to display text and widgets on the Presto enabling me to use much the same code for both these displays.
@SirFico Thank you very much for your reply. I tried to find your post for the Presto with high res fonts. I did not find it. Can you pass me a link please?
@paulsk I’ll add the code here as I just quickly plonked on a couple more text lines, one with a larger font size and another with a different font as the other post I made only used one font and text size and was shown to show text being put on buttons. Its only a bit of test code where I was checking out how to use multiple logical screens on the Presto
The fonts I used were Roboto-Medium.af and cherry-hq.af both of which came from the pimoroni presto examples and of course you would need to put them on the Presto for the code to work. In the example I created a GuiLabel class and created instances to display the text, but that was just for my testing.
from presto import Presto
from picovector import PicoVector, Polygon, Transform, ANTIALIAS_X16
import asyncio
import time
presto = Presto(full_res=True)
display = presto.display
vector = PicoVector(display)
t = Transform()
vector.set_transform(t)
vector.set_antialiasing(ANTIALIAS_X16)
vector.set_font("Roboto-Medium.af", 40)
vector.set_font_letter_spacing(100)
vector.set_font_word_spacing(100)
touch = presto.touch
RED = display.create_pen(200, 0, 0)
BLACK = display.create_pen(0, 0, 0)
GREY = display.create_pen(50, 50, 50)
LIGHTGREY = display.create_pen(100, 100, 100)
WHITE = display.create_pen(255, 255, 255)
SWHITE = display.create_pen(150, 150, 150)
PALEGREEN = display.create_pen(135, 159, 169)
DARKGREY = display.create_pen(10, 40, 50)
GREEN = display.create_pen(14, 60, 76)
PINK = display.create_pen(200,100,100)
BLUE = display.create_pen(0,0,255)
YELLOW = display.create_pen(180,180,0)
class GuiManager:
def __init__(self):
self.screens = {}
self.current_screen = None
def add_screen(self, name, bg_colour):
self.screens.update({name: {'bg_colour': bg_colour, 'widgets':[]}})
if self.current_screen is None:
self.current_screen = name
def set_screen(self, name):
if name in self.screens:
self.current_screen = name
else:
print('error - screen name not found')
def add_widget(self, screen_name, widget_obj):
if screen_name in self.screens:
if widget_obj not in self.screens[screen_name]['widgets']:
self.screens[screen_name]['widgets'].append(widget_obj)
widget_obj.gui_manager = self
widget_obj.assigned_screen = screen_name
else:
print('error - screen name not found')
def blank_gui(self):
display.set_pen(self.screens[self.current_screen]['bg_colour'])
display.clear()
def draw_gui(self):
if self.current_screen is None:
return
self.blank_gui()
for widget in self.screens[self.current_screen]['widgets']:
widget.show()
presto.update()
def draw_widget(self, widget):
pass
def process_touch(self,x,y):
for widget in self.screens[self.current_screen]['widgets']:
if widget.process_touch(x,y):
return
class GuiWidget:
def __init__(self, x, y, w = None, h = None, fg_colour=WHITE, bg_colour=BLACK):
self.x = x
self.y = y
self.w = w
self.h = h
self.fg_colour = fg_colour
self.bg_colour = bg_colour
self.gui_manager = None
self.poly = Polygon()
self.poly_exists = False
self.assigned_screen = None
def show(self):
pass
def within_bounds(self, x, y, w=None, h=None, pad=10):
"""Check if (x,y) is within padded bounds of this control."""
w = w or self.w
h = h or self.h
pad = pad
return (self.x - pad <= x < self.x + w + pad) and (self.y - pad <= y < self.y + h + pad)
def process_touch(self,x,y):
return False
class GuiButton(GuiWidget):
def __init__(self, x, y, w, h, fg_colour, bg_colour=WHITE, text=None,
text_colour=BLACK, flash_colour=None,font="Roboto-Medium.af", font_size=40, r_corners=False, callback=None):
super().__init__(x, y, w, h, fg_colour, bg_colour)
self.text = text
self.text_colour = text_colour
self.flash_colour = flash_colour
self.font = font
self.font_size = font_size
self.r_corners = r_corners
self.callback = callback
def show(self):
display.set_pen(self.fg_colour)
if self.poly_exists == False:
if self.r_corners is True:
self.poly.rectangle(self.x, self.y, self.w, self.h, corners=(10,10,10,10))
else:
self.poly.rectangle(self.x, self.y, self.w, self.h)
self.poly_exists=True
vector.draw(self.poly)
# put text on the button
if self.text is not None:
vector.set_font(self.font, self.font_size)
display.set_pen(self.text_colour)
vector.text(self.text, self.x + 30, self.y + 30)
async def flash(self):
if self.flash_colour:
button_colour=self.fg_colour
self.fg_colour=self.flash_colour
self.show()
presto.update()
await asyncio.sleep(0.25)
self.fg_colour=button_colour
self.show()
presto.update()
def process_touch(self,x,y):
if self.within_bounds(x,y):
asyncio.create_task(self.flash())
if self.callback:
self.callback(self.gui_manager)
return True
class GuiLabel(GuiWidget):
def __init__(self, x, y, w=None, h=None, fg_colour=None, bg_colour=None, text='', text_colour=WHITE,
font="Roboto-Medium.af", font_size=40):
super().__init__(x, y, w, h, fg_colour, bg_colour)
self.text = text
self.text_colour = text_colour
self.font = font
self.font_size = font_size
def show(self):
vector.set_font(self.font, self.font_size)
display.set_pen(self.text_colour)
vector.text(self.text, self.x, self.y)
def set_text(self,new_text):
self.text = new_text
if self.gui_manager.current_screen == self.assigned_screen:
self.gui_manager.draw_gui()
# functions to run on button presses
def b1_callback(mgr):
mgr.set_screen('screen2')
mgr.draw_gui()
def b2_callback(mgr):
mgr.set_screen('screen1')
mgr.draw_gui()
def b3_callback(mgr):
l_3.set_text('Now the label is ??')
def b4_callback(mgr):
l_3.set_text('Another change!!')
# an async task to get touch events and pass coordinates to gui_manager
async def get_touch(mgr):
while True:
touch.poll()
if touch.state:
mgr.process_touch(touch.x, touch.y)
await asyncio.sleep(0.5) # adjust as required to avoid multiple touch events for one press
await asyncio.sleep_ms(100)
async def main(mgr):
# create the touch task
asyncio.create_task(get_touch(mgr))
# display the nominated current screen
mgr.draw_gui()
while True:
await asyncio.sleep(0)
#create a gui manager
mgr = GuiManager()
# create screens
mgr.add_screen('screen1',BLUE)
mgr.add_screen('screen2',BLACK)
# create widgets
b_1 = GuiButton(10,400,300,40,PINK,text='Goto Screen 2',text_colour=BLACK, r_corners=True, callback=b1_callback)
b_2 = GuiButton(10,400,300,40,PINK,text='Goto Screen 1',text_colour=BLACK, r_corners=True, callback=b2_callback)
b_3 = GuiButton(10,200,350,40,PALEGREEN,text='Change Text on S1',text_colour=BLACK, flash_colour=RED, r_corners=True, callback=b3_callback)
b_4 = GuiButton(10,300,250,40,PALEGREEN,text='Change Text',text_colour=BLACK,flash_colour=RED, r_corners=True, callback=b4_callback)
l_1 = GuiLabel(130,75, text='Screen 1')
l_2 = GuiLabel(130,75, text='Screen 2')
l_3 = GuiLabel(100,200, text='test label')
l_4 = GuiLabel(10,120, text='large text',text_colour=PINK, font_size=60)
l_5 = GuiLabel(10,160, text='small and quirky',text_colour=BLACK, font="cherry-hq.af", font_size=50)
# add widgets to screens
mgr.add_widget('screen1', b_1)
mgr.add_widget('screen1', l_1)
mgr.add_widget('screen1', l_3)
mgr.add_widget('screen1', b_4)
mgr.add_widget('screen1', l_4)
mgr.add_widget('screen1', l_5)
mgr.add_widget('screen2', b_2)
mgr.add_widget('screen2', l_2)
mgr.add_widget('screen2', b_3)
# set the current screen
mgr.set_screen('screen1')
try:
asyncio.run(main(mgr))
except KeyboardInterrupt:
print('Ctl C')
finally:
print('finally is being actioned')
asyncio.new_event_loop()
Thank you for your quick reply!
Hi @SirFico here are the results of a first conversion in hi-res, using some of your vector.text settings. Photos
@paulsk Very nice. Much easier on the eye, looking good! If you look at the pimoroni info you see they have a way to create .af fonts to your desires should you want a larger variety of fonts for the Presto vector library.
My preference is to use the Peter Hinch font-to-py for creating fonts, though to use them on the Presto you need to use his writer.py class. If you want an example of using such fonts created with font-to-py then I’ve just uploaded an example to my github - GitHub - beetlegigg/Presto: Examples of using Presto
This example both uses the writer.py for displaying fonts, but also uses the Presto direct to framebuffer option for creating the other widgets.
I did wonder why some of the examples for the Presto display produced by pimoroni use the low res text and when I first fired up my Presto I was a bit aghast at the blocky fonts on display. I see that pimoroni indicate that their base graphics lib runs faster than the vector lib which I think runs on top of the graphics lib, so maybe that was why. In the small tests I have done the Presto Vector lib seems to run ok, though I did find a slightly more responsive screen when using the setup in the example I link to. (plus it has a whole bunch of fonts created via font-to-py to play with :-))
Thank you for your reply.
Concerning speed. That is not an issue for my MQTT project because the messages are sent once a minute, beside some messages containing commands to switch on/off the ambient LED’s or to change the color ot the display text.
I am familiar with Peter Hinch’s font-to-py.
Question: do you know howto update the screen without having this effect of vertical lines at the moment of clearing the screen? It should be possible to clear the screen buffer(s) and then update the screen.
@paulsk The only effect I’ve seen on my Presto is that on a soft reboot, when the new program is then started, the last displayed screen from the previous program briefly gets displayed before the new program clears the screen and displays the new. In the program I posted here for you, whilst there is not much on the screen the program will jump between screen1 and screen2 on the tapping of the on screen change ‘button’. This clears the screen currently displayed screen and then displays the other one. No untoward effects are shown and the change is quick and clean. I’ve not noticed any vertical lines with other stuff that I’ve done on the Presto though I’ve not extensively used it as its going to be used just for a home information monitor fed via mqtt messages, so nothing too demanding.
If you have a chance perhaps run the program I posted and see if the screen change works as expected. Alternatively post a minimal program that illustrates your issue and I will test it on my Presto to see if I also get vertical lines on a screen update.
@SirFico Thank you. I’ll try to create a small program that shows the effect. I spoke about “lines” however it appears as if the letters are “stretched” downward rapidly. This effect I also saw on the SQUiXL during uploads after a build using VSCode/PlatformIO.
@SirFico. Here two photos on which the artifacts are visible: photos.
I think that this happens at moments when the script writes data to the SD-Card.
@paulsk That does look horrible. I’m guessing you receive data via mqtt, update the screen, then write the data to the SD card at which time it then mucks up the display? Tomorrow I will insert an SD card into my Presto to see what damage I can do, but could you confirm your sequence of events.
Have you tried the latest Presto uf2?
This includes an improved display driver which should help with the problem you are experiencing.
@paulsk A quick test of a couple of logical screens with some text thereon. The screens could be swapped on a button push and the text updated on a different button push. I ran a simple file write to the SD card which ran every second (about 20 characters for each write) whilst stabbing the buttons to change the screen and to update some text on one of the screens. I had no problem with the display during this simple test, but I’m open to suggestions for a more comprehensive check.
@MikeB_50 Thanks for the update on a new release of the firmware, I had missed that, and I’ve just updated to the latest, lets hope it solves paulsks problem.