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