E-Paper Display

Dieses E-Paper Display dient dazu, gewisse Messwerte vom openHAB Server schnell und einfach abzulesen, ohne das Smartphone hervorzunehmen, die App öffnen und zur gewünschten Information zu navigieren. Zum Beispiel kann man das Display im Eingangsbereich platzieren wo man Zeit und Temperatur schnell ablesen möchte.

So sieht das Gehäuse mit dem eingbauten Display aus. Es ist absichtlich 45 Grad geneigt, damit es optimal lesbar ist. Die Datei für den 3D-Druck kann hier heruntergeladen werden: https://www.printables.com/model/1045546-epaper-213-pi-pico-3xaa-case

Das Gehäuse ist so kompakt, dass es gerade die Länge des Displays und die Breite des Batteriehalters hat. Alle Komponenten passen so genau hinein, dass sie nicht zusätzlich befestigt werden müssen.

Ein Batteriehalter für 3 AA Mignon Batterien versorgt den Pico und das Display für viele Tage mit Energie, sofern das ganze richtig programmiert ist. Verbunden wird der Pico am VSYS mit dem Pluspol und GND mit dem Minuspol.

Um die Daten im OpenHAB bereitzustellen, habe ich ein Item erstellt und fülle die Werte im JSON Format in einen Text ab.

var actualTemp = (HTTPsrgssr_ActualTemp.state as Number).intValue;
var sunHoursToday = (HTTPsrgssr_Sunhourstoday.state as Number).intValue;
var probPcp = (HTTPsrgssr_ProbPcp.state as Number).intValue;
var tempMax = (HTTPsrgssr_TempMax.state as Number).intValue;
var tempMin = (HTTPsrgssr_TempMin.state as Number).intValue;
var uvi = (HTTPsrgssr_UVI.state as Number).intValue;
var dayOfWeek = now.getDayOfWeek().toString;
dayOfWeek = dayOfWeek.substring(0,1).toUpperCase() + dayOfWeek.substring(1).toLowerCase();

var json = '{'
+'"dayOfWeek":"'+dayOfWeek+'"'
+',"month":'+now.getMonthValue()
+',"day":'+now.getDayOfMonth()
+',"hour":'+now.getHour()
+',"minute":'+now.getMinute()
+',"actualTemp":'+actualTemp
+',"sunHoursToday":'+sunHoursToday
+',"probPcp":'+probPcp
+',"tempMax":'+tempMax
+',"tempMin":'+tempMin
+',"uvi":'+uvi
+'}';

//logInfo("updateWeatherJson", "json: {}", json);
weatherJson.postUpdate(json)

Als Resultat habe ich dann alle Werte in einem JSON Text, welches dann vom Pico gelesen wird. Der Inhalt wieht dan z.B. so aus:

{
"dayOfWeek": "Tuesday",
"month": 11,
"day": 26,
"hour": 11,
"minute": 5,
"actualTemp": 6,
"sunHoursToday": 3,
"probPcp": 33,
"tempMax": 10,
"tempMin": 1,
"uvi": 0
}

Daher auch die Uhrzeit vorkommt, wird die Regel, welche die Daten auffrischt, jede Minute gestartet. Somit ist der Inhalt auch stets aktuell.

Als Beispiel unten mein Python Code für den Raspberry Pico. Die Lib vom Display kann hier heruntergeladen werden: https://github.com/waveshareteam/Pico_ePaper_Code/blob/main/python/Pico_ePaper-2.13_V4.py

WICHTIG: machine.lightsleep funktioniert ab Version 1.24.x von MicroPython nicht mehr korrekt. Also umbedingt die Version 1.23.x installieren.

from epaper2in13v4 import EPD_2in13_V4_Landscape
import network, requests, re, array, machine, time, json, gc

network.hostname("PicoW E-Paper")
vsys = machine.ADC(3)
epd = EPD_2in13_V4_Landscape()
epd.Clear()

# draws black decimal numbers in the 7 segment format
# start_x and start_y are the coords for the upper left start point
# text ist the chars to display. only the chars '0123456789,.:°%/' are supported
# digit_width the width in pixels of one digit, the height is always twice the width
# line_width ist the thickness in pixel of the char lines
# space ist the amount of pixels inbetween the chars
def drawNumber(start_x, start_y, text, digit_width, line_width, space):

r1 = int(digit_width//2-line_width)
r2 = int(digit_width//2-line_width-line_width//2)

for digit in text:
if digit in ['2','3','5','6','7','8','9','0']:
#horizontal top
epd.fill_rect(start_x, start_y, digit_width+line_width, line_width, 0x00)
if digit in ['-','2','3','4','5','6','8','9']:
#horizontal middle
epd.fill_rect(start_x, start_y+digit_width, digit_width, line_width, 0x00)
if digit in ['2','3','5','6','8','9','0']:
#horizontal bottom
epd.fill_rect(start_x, start_y+digit_width*2, digit_width+line_width, line_width, 0x00)
if digit in ['1','4','5','6','8','9','0']:
#vertical left top
epd.fill_rect(start_x, start_y, line_width, digit_width, 0x00)
if digit in ['1','2','6','8','0']:
#vertical left bottom
epd.fill_rect(start_x, start_y+digit_width, line_width, digit_width+line_width, 0x00)
if digit in ['2','3','4','7','8','9','0']:
#vertical right top
epd.fill_rect(start_x+digit_width, start_y, line_width, digit_width+line_width, 0x00)
if digit in ['3','4','5','6','7','8','9','0']:
#vertical right bottom
epd.fill_rect(start_x+digit_width, start_y+digit_width, line_width, digit_width+line_width, 0x00)
if digit == ',':
epd.fill_rect(start_x, start_y + digit_width*2, line_width, line_width+line_width, 0x00)
if digit == '.':
epd.ellipse(start_x, start_y+digit_width*2, r1, r1, 0x00, True)
if digit == ':':
epd.ellipse(start_x, int(start_y+digit_width/2), r1, r1, 0x00, True)
epd.ellipse(start_x, int(start_y+digit_width+digit_width/2), r1, r1, 0x00, True)
if digit in ['°','%']:
epd.ellipse(start_x+r1, start_y+r1, r1, r1, 0x00, True)
epd.ellipse(start_x+r1, start_y+r1, r2, r2, 0xFF, True)
if digit in ['/','%']:
coords = array.array('I', [digit_width+line_width//2, 0, digit_width+line_width, 0, line_width, digit_width*2+line_width, line_width//2, digit_width*2+line_width])
# print("Array: ", coords)
epd.poly(start_x, start_y, coords, 0x00, True)
if digit == '%':
epd.ellipse(start_x+digit_width, start_y+digit_width*2+line_width-r1, r1, r1, 0x00, True)
epd.ellipse(start_x+digit_width, start_y+digit_width*2+line_width-r1, r2, r2, 0xFF, True)
if digit in ['1',',', '.',':', ' ','°']:
start_x += space
else:
start_x += digit_width+space


wlan = network.WLAN(network.STA_IF)
wlan.disconnect()
wlan.active(False)
wlan.deinit()
gc.collect()

while True:

wlan.active(True)
wlan.connect("your-ssid", "your-pwd")

while wlan.isconnected() == False:
print("Connecting")
time.sleep(2)
print(f"WLAN connected: {wlan.isconnected()}")

print(f"WLAN : {wlan.ifconfig()}")

try:
epd.fill(0xff)

response = requests.get("http://<your-openhab-url>/rest/items/weatherJson/state", timeout=5)

print("response.text: ", response.text)
weatherData = json.loads(response.content)
print("weatherData: ", weatherData)
hour = weatherData['hour']
minute = weatherData['minute']
day = weatherData['day']
month = weatherData['month']
dayOfWeek = weatherData['dayOfWeek']

dateStr = f"{day}.{month}."
timeStr = f"{hour}:{minute:02d}"
print("dateStr: ",dateStr)
epd.text(dayOfWeek,5,18,0x00)
drawNumber(90,10,dateStr,10,3,8)
epd.text(timeStr,210,8,0x00)

machine.Pin(29, machine.Pin.IN)
voltage = round(vsys.read_u16()*3.23*3/65535, 1)
print("voltage: ",voltage)
epd.text(f"{voltage}V",210,18,0x00)

actualTemp= f"{weatherData['actualTemp']}°"
print("actualTemp: ", actualTemp)
epd.text("Temp",5,63,0x00)
drawNumber(50,55,actualTemp,10,3,8)

tempMax = f"{weatherData['tempMax']}°"
epd.text("max",100,63,0x00)
drawNumber(130,55,tempMax,10,3,8)

tempMin = f"{weatherData['tempMin']}°"
epd.text("min",170,63,0x00)
drawNumber(200,55,tempMin,10,3,8)

rain = f"{weatherData['probPcp']}%"
print("rain: ", rain)
epd.text("Rain",5,108,0x00)
drawNumber(50,100,rain,10,3,8)

sun = f"{weatherData['sunHoursToday']}"
print("sun: ", sun)
epd.text("Sun",100,108,0x00)
drawNumber(130,100,sun,10,3,8)

uvi = f"{weatherData['uvi']}"
print("uvi: ", uvi)
epd.text("UV",170,108,0x00)
drawNumber(200,100,uvi,10,3,8)

epd.display(epd.buffer)

wlan.disconnect()
wlan.active(False)
wlan.deinit()
gc.collect()

#sleep until 5 min past the hour
minToNextUpdate = 5-minute
if minToNextUpdate <= 0:
minToNextUpdate += 60
print("minToNextUpdate: ", minToNextUpdate)
machine.lightsleep(minToNextUpdate*60*1000)

except Exception as err:
print(f"error: {err}")

Die Bauteile können beispielsweise hier erworben werden: