Pyportal titano keeps restrating when trying to write/edit files

Hi all,

I’ve successfully got this tutorial working
LINK
however now every time I try to write or edit any file or modify a py script on it and save, the device restarts (with the file itself on the device not being modified after it reboots) - so I’ve kind of locked myself out of making any edits to the files or code on it!

I’ve been searching for how to hard reset the pyportal titano, reflashed/updated the firmware, tried older firmwares and have had no luck! any ideas? Thank you :smiley:

(have also tried multiple premium data cables, and followed this link with no success)

Hey Tom,

That sucks. It always seems that as soon as you have one issue fixed another one pops up to cause trouble. I had a look through the other forum post but its not super clear where you ended up with your code and hardware setup.

Could you post your current code and maybe some photos of how you have the PyPortal wired up?

Hey Sam,

Sure; nothing wired up (just powered by USB and in a 3D printed case) and this is the code I’ve got on the device at the moment when opening code.py in Mu Editor:

import time
import board
import displayio
import busio
from digitalio import DigitalInOut
from analogio import AnalogIn
import neopixel
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_display_shapes.rect import Rect
from adafruit_bitmap_font import bitmap_font
from adafruit_button import Button
import adafruit_touchscreen
import adafruit_minimqtt.adafruit_minimqtt as MQTT

OUTPUT_DEBUG_TEXT = True

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define and setup all the sensors and computer-to-computer I/O hardware

ESP = adafruit_esp32spi.ESP_SPIcontrol(
    busio.SPI(board.SCK, board.MOSI, board.MISO),
    DigitalInOut(board.ESP_CS),
    DigitalInOut(board.ESP_BUSY),
    DigitalInOut(board.ESP_RESET),
    debug = OUTPUT_DEBUG_TEXT
)

TOUCHSCREEN = adafruit_touchscreen.Touchscreen(
    board.TOUCH_XL,
    board.TOUCH_XR,
    board.TOUCH_YD,
    board.TOUCH_YU,
    size=(480, 320)
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Prepare the ESP chip to connect to WiFi

try:
    from secrets import secrets
except ImportError:
    if OUTPUT_DEBUG_TEXT:
        print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

wifiNet = ESPSPI_WiFiManager(
    ESP,
    secrets,
    neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2),
    5,
    ESPSPI_WiFiManager.NORMAL,
    OUTPUT_DEBUG_TEXT
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Prepare the display

# Create a display and group
display = board.DISPLAY
group = displayio.Group()

# Having the button parameters defined as constants makes it easy to adjust their size and spacing in just one place.
# These numbers will allow for a lot of "meaningless" space around the buttons which is useful when the screen must
#  be touched to turn on but you don't want to accidentally press a button while you can't see the screen.
BUTTON_WIDTH = 200
BUTTON_HEIGHT = 120
BUTTON_MARGIN = 10

# Create four fancy buttons in an array using the parameters defined above...
FONT = bitmap_font.load_font("/fonts/Helvetica-Bold-16.bdf")
FONT.load_glyphs(b"abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890- ()")
buttons = [
    Button(
        x=BUTTON_MARGIN,
        y=BUTTON_MARGIN,
        width=BUTTON_WIDTH,
        height=BUTTON_HEIGHT,
        label="Fan",
        label_font=FONT,
        style=Button.SHADOWROUNDRECT,
        label_color=0x505050,
        fill_color=0x00FF00,
        outline_color=0x464646,
    ),
    Button(
        x=BUTTON_MARGIN * 2 + BUTTON_WIDTH,
        y=BUTTON_MARGIN,
        width=BUTTON_WIDTH,
        height=BUTTON_HEIGHT,
        label="Blinds",
        label_font=FONT,
        style=Button.SHADOWROUNDRECT,
        label_color=0x505050,
        fill_color=0xFF0000,
        outline_color=0x464646,
    ),
    Button(
        x=BUTTON_MARGIN,
        y=BUTTON_MARGIN * 2 + BUTTON_HEIGHT,
        width=BUTTON_WIDTH,
        height=BUTTON_HEIGHT,
        label="Aircon",
        label_font=FONT,
        style=Button.SHADOWROUNDRECT,
        label_color=0x505050,
        fill_color=0x0000FF,
        outline_color=0x464646,
    ),
    Button(
        x=BUTTON_MARGIN * 2 + BUTTON_WIDTH,
        y=BUTTON_MARGIN * 2 + BUTTON_HEIGHT,
        width=BUTTON_WIDTH,
        height=BUTTON_HEIGHT,
        label="Lamp",
        label_font=FONT,
        style=Button.SHADOWROUNDRECT,
        label_color=0x505050,
        fill_color=0xF2D00D,
        outline_color=0x464646,
    )
]

# Put all buttons into the display group
for button in buttons:
    group.append(button)

# Show the group, which now contains the buttons, on the display
##display.show(group)
display.root_group=group
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define the MQTT topics, tell MQTT to use the ESP chip, and prepare to connect to the broker

MQTT_BUTTON_ONE = "home/bedroom/fan"
MQTT_BUTTON_TWO = "home/bedroom/blinds"
MQTT_BUTTON_THREE = "home/bedroom/aircon"
MQTT_BUTTON_FOUR = "home/bedroom/lamp"

MQTT.set_socket(socket, ESP)

client = MQTT.MQTT(
    broker=secrets["broker"],
    port=1883,
    username=secrets["user"],
    password=secrets["pass"],
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Prepare some stuff to be used during the user-interface loop

# Map each button to the message it must publish when touched
button_message = [MQTT_BUTTON_ONE, MQTT_BUTTON_TWO, MQTT_BUTTON_THREE, MQTT_BUTTON_FOUR]

# At the start of each UI cycle, these values have the described meanings:
#  >=0 means the index of a button which was being touched during the last cycle
#  -1 means a non-button part of the screen was being touched during the last cycle
#  -2 means the screen was not being touched during the last cycle
# Note: this concept assumes a single touch point; multi-touch gestures are not supported
button_was_touched = -2  # Initialise assuming the screen is not being touched on power-up

# When given the result of TOUCHSCREEN.touch_point, this function returns the index of a button,
#  or -1 if no button is touched, or -2 if the screen isn't touched at all.
# Note: this concept assumes a single touch point; multi-touch gestures are not supported
def indexOfTouchedButton(touch=None):
    if touch:
        for butIdx, button in enumerate(buttons):
            if button.contains(touch):
                return butIdx
        return -1
    return -2

# Define how many seconds to wait, while the screen is not being touched, before turning off the display to save power
POWERSAVE_DELAY = 15.0

# Define how many seconds to sleep, reducing CPU usage, before starting the next UI loop checking for touches
DEBOUNCE_DELAY = 0.15

powerSaveCountdown = POWERSAVE_DELAY

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Finally, here is the user-interface event-handling loop which never ends

while True:
    time.sleep(DEBOUNCE_DELAY)  # Sleep briefly to debounce buttons and reduce CPU usage
    touched = indexOfTouchedButton(TOUCHSCREEN.touch_point)
    if touched <= -2:
        # The screen is not being touched...
        if powerSaveCountdown > 0:
            powerSaveCountdown -= DEBOUNCE_DELAY
        elif display.brightness > 0.5:
            display.brightness = 0
    else:
        # The screen is being touched...
        if display.brightness < 0.5:
            display.brightness = 1
            powerSaveCountdown = POWERSAVE_DELAY
            # BIG QUESTION: Having turned the screen on, before testing for a button being touched, should I ...
            #  A) sleep for a fixed duration? (Say, four times the DEBOUNCE_DELAY)
            #  B) watch indefinitely for finger removal?
            #  C) combine options A & B? (Wait for finger removal until some duration has elapsed)
            # In this version, the user has asked to sleep for 0.5 seconds
            time.sleep(0.5)
            touched = indexOfTouchedButton(TOUCHSCREEN.touch_point)
            button_was_touched = -2  # Pretend the user was not touching the screen a moment ago
        if (touched >= 0) and (touched != button_was_touched):
            # A button that wasn't being touched during the last cycle is being touched now...
            data = "OFF" if buttons[touched].selected else "ON"
            if OUTPUT_DEBUG_TEXT:
                print(f"Publishing data {data} to topic {button_message[touched]} associated with button {touched + 1}")
            for attempt in range(5):
                try:
                    try:
                        client.publish(button_message[touched], data)
                    except:
                        try:
                            client.connect()
                        except:
                            try:
                                wifiNet.connect()
                            except:
                                wifiNet.reset()
                                time.sleep(1)  # Sleep for a second to ensure the reset is complete before continuing
                                raise
                            raise
                        raise
                except Exception as err:
                    if OUTPUT_DEBUG_TEXT:
                        print(repr(err))
                else:
                    buttons[touched].selected = not buttons[touched].selected
                    break
            else:
                if OUTPUT_DEBUG_TEXT:
                    print("Publish attempt failed after multiple attempts")
    button_was_touched = touched

Hey Tom,

Nothing stands out as being an easily identifiable issue in your code. I have done some digging and the Adafruit troubleshooting page may have an answer for you.
You can turn off the auto restart by adding this import statement “import supervisor” to your file and ending it with “supervisor.runtime.autoreload = False”.
This should stop the auto restart that happens whenever you upload your code.

Keep in mind this device wants to restart to properly run changes to its code so you may need to experiment with doing this manually with the physical reset button.

Let me know how that goes!
Sam

Hey Sam, I couldnt modify the code with that as it drops out when I try to access it BUT i did see on that link you sent about how to boot into safe mode, which has let me modify the files again!! thank you so much!

Hey Tom,

That’s great to hear! Glad your up and running again.

If you run into issues in the future we are always here to help!
Sam