PyPortal Pynt User Interface example

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.
User avatar
oldblackcrow
 
Posts: 221
Joined: Tue Jun 20, 2017 5:54 pm

Re: PyPortal Pynt User Interface example

Post by oldblackcrow »

Thanks! I'll look through that tomorrow evening. I appreciate your help!

User avatar
oldblackcrow
 
Posts: 221
Joined: Tue Jun 20, 2017 5:54 pm

Re: PyPortal Pynt User Interface example

Post by oldblackcrow »

Well, I've temporarily gave up on the AdafruitIO HTTP example to try the MQTT "Make it your own" at the end of PyPortal UI example.

First, I'm not quite sure what the difference is (aside from the coding statements), as there is no good documentation on the Adafruit site to explain.

Second, when I pasted the code from the website (https://learn.adafruit.com/making-a-pyp ... t-your-own), I have to change the "username" and "password" to "aio_username" and "aio_key" respectively. According to other sources, I gather that the broker is one of the following:
1. https://io.adafruit.com/oldblackcrow/feeds/photocell
2. https://io.adafruit.com/oldblackcrow/da ... light-test
3. https://io.adafruit.com/oldblackcrow/feeds/onoff

I've changed the broker to each of these with the *same* following error:
code.py output:
No SD card found: no SD card
Connecting to WiFi...
Connecting to AP Computer
Connected!
Traceback (most recent call last):
File "code.py", line 71, in <module>
File "adafruit_minimqtt/adafruit_minimqtt.py", line 316, in connect
File "adafruit_minimqtt/adafruit_minimqtt.py", line 316, in connect
MMQTTException: ('Invalid broker address defined.', RuntimeError('Failed to request hostname',))

Code done running.

I've read through the pages, but find nothing that addresses this... of course, I may be missing something.
Any ideas? Thanks!

User avatar
oldblackcrow
 
Posts: 221
Joined: Tue Jun 20, 2017 5:54 pm

Re: PyPortal Pynt User Interface example

Post by oldblackcrow »

Hey guys, I'm back with this next issue. It took a while to get the PyPortal to connect using MQTT, but y'all finally got me connected (viewtopic.php?f=56&t=177198&start=15). Thank you!

However, now I'm integrating that into the UI example (https://learn.adafruit.com/making-a-pyp ... -full-code)

Here is the MQTT example that @brubell supplied but I just modified to slim it down and only have it pull and print from my feed:

Code: Select all

import time
import board
import microcontroller
import displayio
import busio
from digitalio import DigitalInOut
import neopixel
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from analogio import AnalogIn
import neopixel
import adafruit_adt7410
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
from adafruit_button import Button
import adafruit_touchscreen
from adafruit_pyportal import PyPortal
from secrets import secrets

# ------------- Inputs and Outputs Setup ------------- #
try:  # attempt to init. the temperature sensor
    i2c_bus = busio.I2C(board.SCL, board.SDA)
    adt = adafruit_adt7410.ADT7410(i2c_bus, address=0x48)
    adt.high_resolution = True
except ValueError:
    # Did not find ADT7410. Probably running on Titano or Pynt
    adt = None

# WIFI - If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets)

# Setup a feed named 'gas_sensor' for subscribing to changes
gas_sensor = secrets["aio_username"] + "/feeds/rx"

def connected(client, userdata, flags, rc):
    # This function will be called when the client is connected successfully to the broker.
    print("Connected to AIO %s" % gas_sensor)
    # Subscribe to all changes on the gas_sensor.
    client.subscribe(gas_sensor)


def disconnected(client, userdata, rc):
    # This method is called when the client is disconnected
    print("Disconnected from AIO!")


def message(client, topic, message):
    # This method is called when a topic the client is subscribed to has a new message.
    print("Gas Sensor Readings {0}: {1}".format(topic, message))
    
# Connect to WiFi
print("Connecting to WiFi...")
wifi.connect()
print("Connected!")

# Initialize MQTT interface with the esp interface
MQTT.set_socket(socket, esp)

# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
    broker="io.adafruit.com",
    username=secrets["aio_username"],
    password=secrets["aio_key"],
)

mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message

# Connect the client to the MQTT broker.
print("Connecting to AIO...")
mqtt_client.connect()

# init. the light sensor
light_sensor = AnalogIn(board.LIGHT)

pixel_pin = board.D3
num_pixels = 1
ORDER = neopixel.RGB

pixels = neopixel.NeoPixel(
    pixel_pin, num_pixels, brightness=1.0, auto_write=True, pixel_order=ORDER
)

# ---------- Sound Effects ------------- #
soundDemo = '/sounds/sound.wav'
soundBeep = '/sounds/beep.wav'
soundTab = '/sounds/tab.wav'

# ------------- Other Helper Functions------------- #
# Helper for cycling through a number set of 1 to x.
def numberUP(num, max_val):
    num += 1
    if num <= max_val:
        return num
    else:
        return 1

# ------------- Screen Setup ------------- #
pyportal = PyPortal()
display = board.DISPLAY
display.rotation = 0

# Backlight function
# Value between 0 and 1 where 0 is OFF, 0.5 is 50% and 1 is 100% brightness.
def set_backlight(val):
    val = max(0, min(1.0, val))
    board.DISPLAY.auto_brightness = False
    board.DISPLAY.brightness = val

# Set the Backlight
set_backlight(0.5)

# Touchscreen setup
# ------Rotate 0:
screen_width = 320
screen_height = 240
ts = adafruit_touchscreen.Touchscreen(board.TOUCH_XL, board.TOUCH_XR,
                                      board.TOUCH_YD, board.TOUCH_YU,
                                      calibration=((5200, 59000), (5800, 57000)),
                                      size=(320, 240))


# ------------- Display Groups ------------- #
splash = displayio.Group(max_size=15)  # The Main Display Group
view1 = displayio.Group(max_size=15)  # Group for View 1 objects
view2 = displayio.Group(max_size=15)  # Group for View 2 objects
view3 = displayio.Group(max_size=15)  # Group for View 3 objects

def hideLayer(hide_target):
    try:
        splash.remove(hide_target)
    except ValueError:
        pass

def showLayer(show_target):
    try:
        time.sleep(0.1)
        splash.append(show_target)
    except ValueError:
        pass

# ------------- Setup for Images ------------- #

# Display an image until the loop starts
pyportal.set_background('/images/loading.bmp')


bg_group = displayio.Group(max_size=1)
splash.append(bg_group)


icon_group = displayio.Group(max_size=1)
icon_group.x = 15
icon_group.y = 35
icon_group.scale = 1
view2.append(icon_group)

# This will handel switching Images and Icons
def set_image(group, filename):
    """Set the image file for a given goup for display.
    This is most useful for Icons or image slideshows.
        :param group: The chosen group
        :param filename: The filename of the chosen image
    """
    print("Set image to ", filename)
    if group:
        group.pop()

    if not filename:
        return  # we're done, no icon desired

    image_file = open(filename, "rb")
    image = displayio.OnDiskBitmap(image_file)
    try:
        image_sprite = displayio.TileGrid(image, pixel_shader=displayio.ColorConverter())
    except TypeError:
        image_sprite = displayio.TileGrid(image, pixel_shader=displayio.ColorConverter(),
                                          position=(0, 0))
    group.append(image_sprite)

set_image(bg_group, "/images/BGimage.bmp")

# ---------- Text Boxes ------------- #
# Set the font and preload letters
font1 = bitmap_font.load_font("/fonts/Federation-22.bdf")
font = bitmap_font.load_font("/fonts/StarFleet-24.bdf")
font.load_glyphs(b'abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890- ()')

# Default Label styling:
TABS_X = 5
TABS_Y = 50

# Text Label Objects
feed1_label = Label(font1, text="Text Wondow 1", color=0xE39300, max_glyphs=200)
feed1_label.x = TABS_X+20
feed1_label.y = TABS_Y
view1.append(feed1_label)

feed2_label = Label(font1, text="Text Wondow 2", color=0xFFFFFF, max_glyphs=200)
feed2_label.x = TABS_X
feed2_label.y = TABS_Y
#view2.append(feed2_label)

sensors_label = Label(font1, text="Data View", color=0x03AD31, max_glyphs=200)
sensors_label.x = TABS_X+20
sensors_label.y = TABS_Y
view3.append(sensors_label)

sensor_data = Label(font1, text="Data View", color=0x03AD31, max_glyphs=100)
sensor_data.x = TABS_X+15
sensor_data.y = 65
view3.append(sensor_data)


text_hight = Label(font, text="M", color=0x03AD31, max_glyphs=10)
# return a reformatted string with word wrapping using PyPortal.wrap_nicely
def text_box(target, top, string, max_chars):
    text = pyportal.wrap_nicely(string, max_chars)
    new_text = ""
    test = ""
    for w in text:
        new_text += '\n'+w
        test += 'M\n'
    text_hight.text = test  # Odd things happen without this
    glyph_box = text_hight.bounding_box
    target.text = ""  # Odd things happen without this
    target.y = int(glyph_box[3]/2)+top
    target.text = new_text

# ---------- Display Buttons ------------- #
# Default button styling:
BUTTON_HEIGHT = 40
BUTTON_WIDTH = 80

# We want three buttons across the top of the screen
TAPS_HEIGHT = 40
TAPS_WIDTH = int(screen_width/3)
TAPS_Y = 0

# We want two big buttons at the bottom of the screen
BIG_BUTTON_HEIGHT = int(screen_height/3.2)
BIG_BUTTON_WIDTH = int(screen_width/2)
BIG_BUTTON_Y = int(screen_height-BIG_BUTTON_HEIGHT)

# This group will make it easy for us to read a button press later.
buttons = []

# Main User Interface Buttons
button_view1 = Button(x=0, y=0,
                      width=TAPS_WIDTH, height=TAPS_HEIGHT,
                      label="HANDHELD", label_font=font, label_color=0xff7e00,
                      fill_color=0x5c5b5c, outline_color=0x767676,
                      selected_fill=0x1a1a1a, selected_outline=0x2e2e2e,
                      selected_label=0x525252)
pixels.fill((0, 255, 0))
buttons.append(button_view1)  # adding this button to the buttons group

button_view2 = Button(x=TAPS_WIDTH, y=0,
                      width=TAPS_WIDTH, height=TAPS_HEIGHT,
                      label="SUBSPACE", label_font=font, label_color=0xff7e00,
                      fill_color=0x5c5b5c, outline_color=0x767676,
                      selected_fill=0x1a1a1a, selected_outline=0x2e2e2e,
                      selected_label=0x525252)
buttons.append(button_view2)  # adding this button to the buttons group

button_view3 = Button(x=TAPS_WIDTH*2, y=0,
                      width=TAPS_WIDTH, height=TAPS_HEIGHT,
                      label="SENSORS", label_font=font, label_color=0xff7e00,
                      fill_color=0x5c5b5c, outline_color=0x767676,
                      selected_fill=0x1a1a1a, selected_outline=0x2e2e2e,
                      selected_label=0x525252)
buttons.append(button_view3)  # adding this button to the buttons group

button_switch = Button(x=0, y=BIG_BUTTON_Y,
                       width=BIG_BUTTON_WIDTH, height=BIG_BUTTON_HEIGHT,
                       label="Switch", label_font=font, label_color=0xff7e00,
                       fill_color=0x5c5b5c, outline_color=0x767676,
                       selected_fill=0x1a1a1a, selected_outline=0x2e2e2e,
                       selected_label=0x525252)
#buttons.append(button_switch)  # adding this button to the buttons group

button_2 = Button(x=BIG_BUTTON_WIDTH, y=BIG_BUTTON_Y,
                  width=BIG_BUTTON_WIDTH, height=BIG_BUTTON_HEIGHT,
                  label="Button", label_font=font, label_color=0xff7e00,
                  fill_color=0x5c5b5c, outline_color=0x767676,
                  selected_fill=0x1a1a1a, selected_outline=0x2e2e2e,
                  selected_label=0x525252)
#buttons.append(button_2)  # adding this button to the buttons group

# Add all of the main buttons to the splash Group
for b in buttons:
    splash.append(b)


# Make a button to change the icon image on view2
button_icon = Button(x=150, y=60,
                     width=BUTTON_WIDTH, height=BUTTON_HEIGHT,
                     label="Icon", label_font=font, label_color=0xffffff,
                     fill_color=0x8900ff, outline_color=0xbc55fd,
                     selected_fill=0x5a5a5a, selected_outline=0xff6600,
                     selected_label=0x525252, style=Button.ROUNDRECT)
#buttons.append(button_icon)  # adding this button to the buttons group

# Add this button to view2 Group
#view2.append(button_icon)

# Make a button to play a sound on view2
button_sound = Button(x=150, y=170,
                      width=BUTTON_WIDTH, height=BUTTON_HEIGHT,
                      label="Sound", label_font=font, label_color=0xffffff,
                      fill_color=0x8900ff, outline_color=0xbc55fd,
                      selected_fill=0x5a5a5a, selected_outline=0xff6600,
                      selected_label=0x525252, style=Button.ROUNDRECT)
#buttons.append(button_sound)  # adding this button to the buttons group

# Add this button to view2 Group
#view3.append(button_sound)

#pylint: disable=global-statement
def switch_view(what_view):
    global view_live
    if what_view == 1:
        hideLayer(view2)
        hideLayer(view3)
        button_view1.selected = False
        button_view2.selected = True
        button_view3.selected = True
        showLayer(view1)
        view_live = 1
        print("View1 On")
    elif what_view == 2:
        # global icon
        hideLayer(view1)
        hideLayer(view3)
        button_view1.selected = True
        button_view2.selected = False
        button_view3.selected = True
        showLayer(view2)
        view_live = 2
        print("View2 On")
    else:
        hideLayer(view1)
        hideLayer(view2)
        button_view1.selected = True
        button_view2.selected = True
        button_view3.selected = False
        showLayer(view3)
        view_live = 3
        print("View3 On")
#pylint: enable=global-statement

# Set veriables and startup states
button_view1.selected = False
button_view2.selected = True
button_view3.selected = True
showLayer(view1)
hideLayer(view2)
hideLayer(view3)

view_live = 1
icon = 1
icon_name = "Ruby"
button_mode = 1
switch_state = 0
button_switch.label = "OFF"
button_switch.selected = True

# Update out Labels with display text.
text_box(feed1_label, TABS_Y,
         "Data provided by AdafuitIO", 30)
text_box(feed1_label, TABS_Y,
         'Handheld GAS Sensor Data.'
         .format(feed1_label.bounding_box[2], feed1_label.bounding_box[2]*2), 15)

text_box(feed2_label, TABS_Y, 'SubSpace Data', 18)

text_box(sensors_label, TABS_Y-20,
         "", 28)

board.DISPLAY.show(splash)

# ------------- Code Loop ------------- #
while True:
    mqtt_client.loop()
    time.sleep(2)
    touch = ts.touch_point
    light = light_sensor.value

    if adt:  # Only if we have the temperature sensor
        tempC = adt.temperature
    else:  # No temperature sensor
        tempC = microcontroller.cpu.temperature

    tempK = tempC + 273.15
    sensor_data.text = 'PHOTONS\n{}\n\nSYSTEM TEMP\n{}°K'.format(light, tempK)

    # ------------- Handle Button Press Detection  ------------- #
    if touch:  # Only do this if the screen is touched
        # loop with buttons using enumerate() to number each button group as i
        for i, b in enumerate(buttons):
            if b.contains(touch):  # Test each button to see if it was pressed
                print('button%d pressed' % i)
                if i == 0 and view_live != 1:  # only if view1 is visable
                    pyportal.play_file(soundTab)
                    switch_view(1)
                    pixels.fill((0, 255, 0))
                    while ts.touch_point:
                        pass
                if i == 1 and view_live != 2:  # only if view2 is visable
                    pyportal.play_file(soundTab)
                    switch_view(2)
                    set_image(icon_group,"/images/Sensor-graphic-subspace.bmp")
                    pixels.fill((255, 0, 0))
                    while ts.touch_point:
                        pass
                if i == 2 and view_live != 3:  # only if view3 is visable
                    pyportal.play_file(soundTab)
                    switch_view(3)
                    pixels.fill((0, 0, 255))
                    while ts.touch_point:
                        pass
                if i == 3:
                    pyportal.play_file(soundBeep)
                    # Toggle switch button type
                    if switch_state == 0:
                        switch_state = 1
                        b.label = "ON"
                        b.selected = False
                        pixel.fill(BLACK)
                        print("Swich ON")
                    else:
                        switch_state = 0
                        b.label = "OFF"
                        b.selected = True
                        pixel.fill(BLACK)
                        print("Swich OFF")
                    # for debounce
                    while ts.touch_point:
                        pass
                    print("Swich Pressed")
                if i == 4:
                    pyportal.play_file(soundBeep)
                    # Momentary button type
                    b.selected = True
                    print('Button Pressed')
                    button_mode = numberUP(button_mode, 5)
                    if button_mode == 1:
                        pixel.fill(RED)
                    elif button_mode == 2:
                        pixel.fill(RED)
                    elif button_mode == 3:
                        pixel.fill(RED)
                    elif button_mode == 4:
                        pixel.fill(BLUE)
                    elif button_mode == 5:
                        pixel.fill(PURPLE)
                    switch_state = 1
                    button_switch.label = "ON"
                    button_switch.selected = False
                    # for debounce
                    while ts.touch_point:
                        pass
                    print("Button released")
                    b.selected = False
                if i == 5 and view_live == 2:  # only if view2 is visable
                    pyportal.play_file(soundBeep)
                    b.selected = True
                    while ts.touch_point:
                        pass
                    print("Icon Button Pressed")
                    icon = numberUP(icon, 3)
                    if icon == 1:
                        icon_name = "Ruby"
                    elif icon == 2:
                        icon_name = "Gus"
                    elif icon == 3:
                        icon_name = "Billie"
                    b.selected = False
                    text_box(feed2_label, TABS_Y,
                             "Every time you tap the Icon button the icon image will \
change. Say hi to {}!".format(icon_name), 18)
                    set_image(icon_group, "/images/"+icon_name+".bmp")
                if i == 6 and view_live == 3:  # only if view3 is visable
                    b.selected = True
                    while ts.touch_point:
                        pass
                    print("Sound Button Pressed")
                    pyportal.play_file(soundDemo)
                    b.selected = False

I'm getting the following error:
code.py output:
Connecting to WiFi...
Connected!
Connecting to AIO...
Connected to AIO oldblackcrow/feeds/rx
Traceback (most recent call last):
File "code.py", line 107, in <module>
File "adafruit_pyportal/__init__.py", line 139, in __init__
ValueError: SCK in use

Code done running.

Press any key to enter the REPL. Use CTRL-D to reload.


So, @brubell said, "Could you try this - it doesn't rely on the PyPortal class at all..." with the above code, it's running into that error because line 107 calls for PyPortal. Is there any way around this? I'm soooooo close to finishing this and this is the only thing holding me back. Thanks so much!

User avatar
oldblackcrow
 
Posts: 221
Joined: Tue Jun 20, 2017 5:54 pm

Re: PyPortal Pynt User Interface example

Post by oldblackcrow »

This issue has been solved! Thanks to @Neradoc#2644 on Discord for the following code: https://gist.github.com/Neradoc/0093bac ... 18e4950f1d

The modification is as such:

Code: Select all

import time
import random
from adafruit_pyportal import PyPortal

pyportal = PyPortal()
pyportal.network.connect()

key = "rx"
latest_id = "rx"

while True:
    feed = pyportal.get_io_feed(key)
    client = pyportal.network._get_io_client()
    last = client.receive_data(key)
    the_id = last['id']
    if the_id != latest_id:
        print("New value:",last['value'])
        latest_id = the_id
    time.sleep(5)


I'll post the entire UI code with the Garmin LIDAR and this modification tomorrow.

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

Return to “Adafruit CircuitPython”