Hi,
I'm trying to get my m5paper up and running with a basic app to toggle a device.
The code displays temperature, humidity, co2, IAQ Level all correctly, but the touch interaction with the device is not working - any tips? Also, am I correctly implementing the sleep?
Thanks so much in advance!
D
import os, sys, io
import M5
from M5 import *
import requests2
import time
import json
# Global data variable
data = {}
data['grow_lamp_state'] = "Unknown" # To store current state
# Configuration
ha_url = "xxx" # Change this to your HA instance
ha_token = "xxx"
screenTitle = 'Office Environment' # Title to display
wakeup_interval = 300 # 5 minutes in seconds
# Button positioning
button_x = 270 # Center X position of button
button_y = 480 # Y position for button (adjust as needed)
button_width = 200 # Width of button
button_height = 60 # Height of button
# Define constants for e-ink (black and white)
WHITE = 0xffffff
BLACK = 0x000000
def fetchHomeAssistantData(entity_id):
url = f"{ha_url}/api/states/{entity_id}"
headers = {
'Authorization': f'Bearer {ha_token}',
'Content-Type': 'application/json'
}
try:
response = requests2.get(url, headers=headers)
if response.status_code == 200:
return json.loads(response.text)
else:
print(f"Error: {response.status_code}")
return None
except Exception as e:
print(f"Error fetching data: {e}")
return None
def toggleGrowLamp():
url = f"{ha_url}/api/services/light/toggle"
headers = {
'Authorization': f'Bearer {ha_token}',
'Content-Type': 'application/json'
}
payload = {
"entity_id": "light.grow_lamp"
}
try:
response = requests2.post(url, headers=headers, data=json.dumps(payload))
if response.status_code == 200:
print("Toggled grow lamp successfully")
# Update our stored state (will be refreshed next time)
data['grow_lamp_state'] = "on" if data['grow_lamp_state'] == "off" else "off"
# Force a re-render
render()
return True
else:
print(f"Error toggling grow lamp: {response.status_code}")
return False
except Exception as e:
print(f"Error toggling grow lamp: {e}")
return False
def aggregateData():
global data
# Device data
try:
data['batteryPercentage'] = str(M5.Power.getBatteryLevel()) + '%'
data['voltage'] = f"{M5.Power.getBatVoltage() / 1000:.2f}V"
except:
data['batteryPercentage'] = "N/A"
data['voltage'] = "N/A"
# Check WiFi
try:
data['isWifiConnected'] = True # Assume connected
except:
data['isWifiConnected'] = True
data['currentTime'] = time.localtime()
# Fetch Home Assistant data
try:
# Temperature from Aranet4
temp_data = fetchHomeAssistantData("sensor.aranet4_2836d_temperature")
if temp_data:
data['ha_temperature'] = temp_data.get("state", "Unknown")
data['ha_temperature_unit'] = temp_data.get("attributes", {}).get("unit_of_measurement", "°C")
else:
data['ha_temperature'] = "Error"
data['ha_temperature_unit'] = ""
# Humidity from Aranet4
humidity_data = fetchHomeAssistantData("sensor.aranet4_2836d_humidity")
if humidity_data:
data['ha_humidity'] = humidity_data.get("state", "Unknown")
data['ha_humidity_unit'] = humidity_data.get("attributes", {}).get("unit_of_measurement", "%")
else:
data['ha_humidity'] = "Error"
data['ha_humidity_unit'] = ""
# CO2 from Aranet4
co2_data = fetchHomeAssistantData("sensor.aranet4_2836d_carbon_dioxide")
if co2_data:
data['ha_co2'] = co2_data.get("state", "Unknown")
data['ha_co2_unit'] = co2_data.get("attributes", {}).get("unit_of_measurement", "ppm")
else:
data['ha_co2'] = "Error"
data['ha_co2_unit'] = ""
# IAQ from office sensor
iaq_data = fetchHomeAssistantData("sensor.office_iaq_level")
if iaq_data:
data['ha_iaq'] = iaq_data.get("state", "Unknown")
else:
data['ha_iaq'] = "Error"
# Grow lamp state
lamp_data = fetchHomeAssistantData("light.grow_lamp")
if lamp_data:
data['grow_lamp_state'] = lamp_data.get("state", "Unknown")
else:
data['grow_lamp_state'] = "Unknown"
except Exception as e:
print(f"Error in aggregateData: {e}")
def addZero(x):
if int(x) < 10:
return '0' + str(x)
return str(x)
def isButtonPressed(x, y):
return (x >= button_x - button_width//2 and
x <= button_x + button_width//2 and
y >= button_y - button_height//2 and
y <= button_y + button_height//2)
def render():
# Clear screen
M5.Lcd.fillScreen(WHITE)
M5.Lcd.setTextColor(BLACK, WHITE)
# Header
M5.Lcd.setTextSize(2)
M5.Lcd.setCursor(12, 10)
M5.Lcd.print(screenTitle)
M5.Lcd.drawLine(0, 40, 540, 40, BLACK)
# Current time in header
t = data['currentTime']
time_str = f"{addZero(t[3])}:{addZero(t[4])}"
date_str = f"{t[0]}-{addZero(t[1])}-{addZero(t[2])}"
M5.Lcd.setCursor(300, 10)
M5.Lcd.print(f"{date_str}")
# Main content area
margin = 20
y_pos = 80
# Temperature display
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print("Temperature:")
# Add indicator based on value
try:
temp_value = float(data['ha_temperature'])
indicator = ""
if temp_value > 25:
indicator = "!" # High
elif temp_value < 18:
indicator = "!" # Low
else:
indicator = "" # Good
except ValueError:
indicator = "?"
M5.Lcd.setTextSize(3)
M5.Lcd.setCursor(250, y_pos)
M5.Lcd.print(f"{data['ha_temperature']}{data['ha_temperature_unit']} {indicator}")
y_pos += 80
# Reset text size
M5.Lcd.setTextSize(2)
# Humidity display
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print("Humidity:")
try:
humidity_value = float(data['ha_humidity'])
indicator = ""
if humidity_value > 60:
indicator = "!" # High
elif humidity_value < 40:
indicator = "!" # Low
else:
indicator = "" # Good
except ValueError:
indicator = "?"
M5.Lcd.setTextSize(3)
M5.Lcd.setCursor(250, y_pos)
M5.Lcd.print(f"{data['ha_humidity']}{data['ha_humidity_unit']} {indicator}")
y_pos += 80
# Reset text size
M5.Lcd.setTextSize(2)
# CO2 display
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print("CO2:")
try:
co2_value = float(data['ha_co2'])
indicator = ""
if co2_value > 1000:
indicator = "!" # Warning
else:
indicator = "" # Good
except ValueError:
indicator = "?"
M5.Lcd.setTextSize(3)
M5.Lcd.setCursor(250, y_pos)
M5.Lcd.print(f"{data['ha_co2']} {data['ha_co2_unit']} {indicator}")
y_pos += 80
# Reset text size
M5.Lcd.setTextSize(2)
# IAQ display
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print("IAQ Level:")
M5.Lcd.setTextSize(3)
M5.Lcd.setCursor(250, y_pos)
M5.Lcd.print(f"{data['ha_iaq']}")
y_pos += 100
# Reset text size
M5.Lcd.setTextSize(2)
# Divider line
M5.Lcd.drawLine(margin, y_pos, 540-margin, y_pos, BLACK)
y_pos += 40
# Grow Lamp Control Button
global button_y
button_y = y_pos # Set the button Y position dynamically
# Draw button
button_color = BLACK if data['grow_lamp_state'] == "on" else WHITE
text_color = WHITE if data['grow_lamp_state'] == "on" else BLACK
M5.Lcd.fillRoundRect(
button_x - button_width//2,
button_y - button_height//2,
button_width,
button_height,
10, # Corner radius
button_color
)
# Draw button border
M5.Lcd.drawRoundRect(
button_x - button_width//2,
button_y - button_height//2,
button_width,
button_height,
10,
BLACK
)
# Button text
M5.Lcd.setTextSize(2)
M5.Lcd.setTextColor(text_color, button_color)
# Calculate centered text position
text = "Grow Lamp: " + ("ON" if data['grow_lamp_state'] == "on" else "OFF")
# Position for centered text
text_width = len(text) * 12 # Approximate width based on font size
text_x = button_x - text_width//2
text_y = button_y - 10 # Adjust for vertical centering
M5.Lcd.setCursor(text_x, text_y)
M5.Lcd.print(text)
# Reset text color
M5.Lcd.setTextColor(BLACK, WHITE)
y_pos += 80 # Add space below button
# Divider line after button
M5.Lcd.drawLine(margin, y_pos, 540-margin, y_pos, BLACK)
y_pos += 40
# Device information
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print("Device Info:")
y_pos += 40
# Use smaller font for device info
M5.Lcd.setTextSize(1)
# Battery
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print(f"Battery: {data['batteryPercentage']}")
y_pos += 30
# Voltage
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print(f"Voltage: {data['voltage']}")
y_pos += 30
# WiFi status
wifi_status = "Connected" if data['isWifiConnected'] else "Disconnected"
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print(f"WiFi: {wifi_status}")
y_pos += 30
# Update interval
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print(f"Update Interval: {int(wakeup_interval/60)} min")
y_pos += 30
# Last update time
t = data['currentTime']
update_time = f"{addZero(t[3])}:{addZero(t[4])}:{addZero(t[5])}"
M5.Lcd.setCursor(margin, y_pos)
M5.Lcd.print(f"Last Updated: {update_time}")
# For e-ink displays
try:
M5.update()
except:
pass
# Main execution
try:
# Initialize
M5.begin()
# Show loading message
M5.Lcd.fillScreen(WHITE)
M5.Lcd.setTextColor(BLACK, WHITE)
M5.Lcd.setTextSize(2)
M5.Lcd.setCursor(200, 240)
M5.Lcd.print("Loading...")
# Try to update display
try:
M5.update()
except:
pass
# Fetch data and render display
aggregateData()
render()
# Keep awake longer to allow interaction
awake_time = 30 # 30 seconds instead of 10
awake_start = time.time()
last_touch_time = 0
# Wait for touch events or timeout
while time.time() - awake_start < awake_time:
# Check for touches using native GT911 method
if hasattr(M5, 'TP'):
# For newer firmware with direct touch access
if hasattr(M5.TP, 'getStatus') and M5.TP.getStatus():
# Get touch point
x = M5.TP.getX()
y = M5.TP.getY()
# Only process touch if it's a new touch (debounce)
current_time = time.time()
if current_time - last_touch_time > 0.5:
if isButtonPressed(x, y):
print(f"Button pressed at {x},{y}")
toggleGrowLamp()
# Reset timeout counter to give more time after interaction
awake_start = time.time()
last_touch_time = current_time
# Alternative method for M5Paper with different touch API
try:
if hasattr(M5, 'TP') and hasattr(M5.TP, 'touched'):
touches = M5.TP.touched()
if touches > 0:
# Process first touch point
point = M5.TP.getPoint(0)
x, y = point.x, point.y
# Only process touch if it's a new touch (debounce)
current_time = time.time()
if current_time - last_touch_time > 0.5:
if isButtonPressed(x, y):
print(f"Button pressed at {x},{y}")
toggleGrowLamp()
# Reset timeout counter to give more time after interaction
awake_start = time.time()
last_touch_time = current_time
except:
pass
# Update display if needed
try:
M5.update()
except:
pass
time.sleep(0.1)
# Try to sleep
try:
M5.Power.deepSleep(wakeup_interval * 1000)
except:
try:
import machine
machine.deepsleep(wakeup_interval * 1000)
except:
while True:
time.sleep(3600) # Just sleep in a loop if deep sleep fails
except Exception as e:
# Display error
M5.Lcd.fillScreen(WHITE)
M5.Lcd.setTextColor(BLACK, WHITE)
M5.Lcd.setTextSize(2)
M5.Lcd.setCursor(20, 200)
M5.Lcd.print(f"Error: {str(e)}")
try:
M5.update()
except:
pass
time.sleep(60)