Domoticz devices bedienen via een ESP32 Touchscreen 4 inch
Onlangs kwam ik op internet een ESP32 Touchscreen schermpje tegen voor slechts twee tientjes. Het leek me wel handig om deze in te zetten om Domoticz switches, via MQTT, te kunnen schakelen. We hebben hier twee aquaria, waarbij de eens in de zoveel dagen het filter en de thermostaat uit moeten zetten, tijdens onderhoud aan de planten of het verversen van water. Ook leek het met wel handig om de watertemperatuur op het schermpje af te kunnen lezen. Na wat onderzoek bleek dat allemaal wel mogelijk te zijn.
In deze handleiding gaan we ervan uit dat je Domoticz en een MQTT broker (server) al werkend hebt.
Python en ESPTool installeren
De eerste stap is om Python te installeren op een Windows computer (uiteraard kun je ook een Linux bak of Mac gebruiken). Ga naar https://www.python.org/downloads/ en download de nieuwste versie door op de gele button te drukken. Op het gedownloade bestand (python-3.x.x.exe). Vink de optie Add Python 3.x to PATH aan en klik op Install Now. Om te controlen of Python goed geïnstalleerd is druk je op Win + R en druk op Enter.
Typ nu py --version in en je moet dan iets zie van Python 3.x.x
Installeer nu ESPTool:
1 |
[crayon-6899f3205f79e152020853 inline="1" lang="python" decode="true" ]py -m pip install esptool |
Scherm flashen
Download nu het bestand tasmota32s3-qio_opi.factory.bin en flash het scherm met deze firmware:
1 |
[crayon-6899f3205f7a0822438942 inline="1" lang="python" decode="true" ]py -m esptool -b 460800 write_flash 0x0 C:\Users\XXXXXX\Downloads\tasmota32s3-qio_opi.factory.bin |

Het scherm zal nog zwart blijven, maar het is nu wel, tijdelijk, een acces point geworden. Maak met je telefoon verbinding met het SSID tasmota-E43FF3-4285 (of iets wat er op lijkt). Ga vervolgens op je telefoon naar 192.168.4.1 (via een browser), kies het juiste WiFi netwerk en vul het wachtwoord in. Klik tot slot op Save.

Het scherm zal nu rebooten en na een paar seconden krijg je te zien dat de WiFi connectie tot stand is gebracht. Ook wordt het IP zichtbaar dat is toegewezen aan het touchscreen (verstandig is om via jouw model het scherm een vast IP-adres te geven):

Ga vervolgens op de PC naar het IP-adres dat aangegeven staat en klik op Firmware Upgrade:

Bij OTA Url vul je in: https://github.com/Jason2866/Tasmota-specials/blob/firmware/firmware/tasmota32/other/tasmota32s3-qio_opi.bin en klik vervolgens op Start upgrade:

Na zo’n 10 seconden is de firmware geïnstalleerd en het scherm opnieuw opgestart. Je ziet dan onderstaand scherm. Klik op Restart:

Scherm configueren
De nieuwe firmware bevat alle functies die we nodig hebben, maar het scherm zal het nog niet doen. Daarvoor moeten we nog een paar stapjes uitvoeren.
Klik nu op Configuration en vervolgens op Other. Plak in het veld Template onderstaande waarde en klik op Save:
1 |
{"NAME":"ESP32S3-4848S040","GPIO":[1,226,225,0,6210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,640,0,0,0,0,0,0,0,992,0,224,672,6720,0,0,608,0,704,736],"FLAG":0,"BASE":1} |

Ga nu naar Configuration en vervolgens naar Module. In het veld Module type selecteer je ESP32S3-4848S040 (0) en klik onderaan op Save:

Het scherm is nog steeds zwart, maak je daar geen zorgen over, dat komt dadelijk wel goed ;). Waarschijnlijk staat het touchscreen nog op een Chinese tijdzone. Tijd om dat aan te passen! Ga naar Tools en vervolgens Console. Kopieer onderstaande regel naar de console en druk op ENTER:
1 |
Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120 |

Nu is het de hoogste tijd om het scherm werkend te krijgen! Download het display.zip bestand en pak dit uit. We hebben het bestand display.ini nodig. Ga nu Tools en vervolgens Manage File system. Selecteer het bestand display.ini en klik op Upload

Je krijgt een melding dat de upload succesvol uitgevoerd is:

Op dezelfde manier upload je het bestand Roboto-Regular.ttf, zodat we een aardig lettertype op het scherm te zien krijgen.
Klik nu op Manage File system, vervolgens op Tools, Main Menu en tot slot op Restart. Tijdens het rebooten zal je een blauw scherm met het Tasmota logo zien. Het scherm werkt (maar zal vrij snel weer zwart worden, omdat we nog niets verder geprogrammeerd hebben)!
Ga nu naar Configuration en vervolgens MQTT. Vul hier het IP-adres van jouw MQTT gateway in en klik op Save:

Layout (pages.jsonl)
We moeten nu nog 2 bestanden uploaden: de eerste is pages.jsonl bestand, hierin staat de layout van de pagina(s) van het scherm, zoals bijvoorbeeld de buttons, switches, maar ook het lettertype, de kleuren, etc. Het tweede bestand is autoexec.be. Hierin staan functies waarmee switches, via MQTT, aangestuurd worden. Laten we beginnen met pages.jsonl. Maak dit bestand aan op de PC met onderstaande code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
{"page":0,"comment":"---------- Upper status line ----------"} {"id":4,"comment":"Wifi indicator","obj":"lv_wifi_arcs","x":424,"y":6,"w":53,"h":33,"radius":0,"border_side":0,"bg_color":"#000000","line_color":"#7cc2ef"} {"id":5,"comment":"Clock","obj":"lv_clock","x":260,"y":3,"w":210,"h":48,"radius":0,"border_side":0,"text_font":"A:Roboto-Regular.ttf-36","text_color":"#7cc2ef"} {"comment":"---------- Bottom buttons - prev/home/next ----------"} {"page":0,"id":1,"obj":"btn","action":"prev","x":0,"y":416,"w":158,"h":64,"text":"\uE141","radius":14,"border_side":0,"bg_color":"#3B478B","text_color":"#FFFFFF","text_font":28} {"page":0,"id":2,"obj":"btn","action":"back","x":160,"y":416,"w":160,"h":64,"text":"\uE2DC","radius":24,"border_side":0,"bg_color":"#234E70","text_color":"#FFFFFF","text_font":28} {"page":0,"id":3,"obj":"btn","action":"next","x":322,"y":416,"w":158,"h":64,"text":"\uE142","radius":14,"border_side":0,"bg_color":"#3B478B","text_color":"#FFFFFF","text_font":28} {"page":1,"comment":"---------- Page 1 Aquarium 1 ----------"} {"id":0,"text_font":"A:Roboto-Regular.ttf-30","bg_color":"#EAF6FB","bg_grad_dir":0,"text_color":"#101820","bg_opa":255} {"id":70,"obj":"label","text":" Aquarium 1","x":0,"y":0,"w":480,"pad_right":90,"h":48,"bg_color":"#2C3EA0","bg_opa":255,"radius":10,"border_side":1,"border_width":0,"border_color":"#FFFFFF","text_font":"A:Roboto-Regular.ttf-34","text_color":"#FFFFFF"} {"id":71,"obj":"label","x":8,"y":80,"w":200,"text":"Verlichting"} {"id":72,"obj":"switch","x":250,"y":75,"w":100,"h":30,"text":"Off","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"id":73,"obj":"label","x":370,"y":80,"w":100,"text":"Status"} {"id":74,"obj":"label","x":8,"y":140,"w":200,"text":"Filter"} {"id":75,"obj":"switch","x":250,"y":135,"w":100,"h":30,"text":"Off","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"id":76,"obj":"label","x":370,"y":140,"w":100,"text":"Status"} {"id":77,"obj":"label","x":8,"y":200,"w":200,"text":"Thermostaat"} {"id":78,"obj":"switch","x":250,"y":195,"w":100,"h":30,"text":"Off","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"id":79,"obj":"label","x":370,"y":200,"w":100,"text":"Status"} {"id":80,"obj":"label","x":8,"y":260,"w":200,"text":"CO2"} {"id":81,"obj":"switch","x":250,"y":255,"w":100,"h":30,"text":"Off","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"id":82,"obj":"label","x":370,"y":260,"w":100,"text":"Status"} {"id":83,"obj":"label","x":8,"y":320,"w":200,"text":"Temperatuur"} {"id":84,"obj":"label","x":250,"y":315,"w":100,"h":30,"text": "--.- °C","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"page":2,"comment":"---------- Page 2 Aquarium 2 ----------"} {"id":0,"text_font":"A:Roboto-Regular.ttf-30","bg_color":"#EAF6FB","bg_grad_dir":0,"text_color":"#101820","bg_opa":255} {"id":70,"obj":"label","text":" Aquarium 2","x":0,"y":0,"w":480,"pad_right":90,"h":48,"bg_color":"#2C3EA0","bg_opa":255,"radius":10,"border_side":1,"border_width":0,"border_color":"#FFFFFF","text_font":"A:Roboto-Regular.ttf-34","text_color":"#FFFFFF"} {"id":71,"obj":"label","x":8,"y":80,"w":200,"text":"Verlichting"} {"id":72,"obj":"switch","x":250,"y":75,"w":100,"h":30,"text":"Off","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"id":73,"obj":"label","x":370,"y":80,"w":100,"text":"Status"} {"id":74,"obj":"label","x":8,"y":140,"w":200,"text":"Filter"} {"id":75,"obj":"switch","x":250,"y":135,"w":100,"h":30,"text":"Off","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"id":76,"obj":"label","x":370,"y":140,"w":100,"text":"Status"} {"id":77,"obj":"label","x":8,"y":200,"w":200,"text":"Thermostaat"} {"id":78,"obj":"switch","x":250,"y":195,"w":100,"h":30,"text":"Off","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"id":79,"obj":"label","x":370,"y":200,"w":100,"text":"Status"} {"id":80,"obj":"label","x":8,"y":260,"w":200,"text":"CO2"} {"id":81,"obj":"switch","x":250,"y":255,"w":100,"h":30,"text":"Off","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} {"id":82,"obj":"label","x":370,"y":260,"w":100,"text":"Status"} {"id":83,"obj":"label","x":8,"y":320,"w":200,"text":"Temperatuur"} {"id":84,"obj":"label","x":250,"y":315,"w":100,"h":30,"text": "--.- °C","bg_color":"#B0C4DE","bg_color20":"#3B478B","bg_color11":"#7cc2ef","text_color":"#222222","text_color01":"#222222"} |
Ga nu naar Tools en vervolgens Manage File system. Selecteer het bestand pages.jsonl en klik op Upload:

Herstart nu het scherm en je zal merken dat we eindelijk wat op het scherm kunnen zien:

Logica (autoexec.be)
Het is leuk dat we nu eindelijk beeld hebben, maar de switches, temperatuur, etc. moet natuurlijk ook continu bijgewerkt worden. Dat doen we met het bestand autoexec.be.
Maak dit bestand aan op de PC met onderstaande code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
import string import haspmota import mqtt import json haspmota.start() #---------------------------------------------- Make the display dim after it has not been touched for 30s. Status: Tested - works good. -----------------------------------------------# import display var dimmerstatus = true display.dimmer(100) # bright is the default when powering up def ina() var d if (dimmerstatus) d = lv.disp().get_inactive_time() if (d > 1000*30) display.dimmer(33) dimmerstatus = false end end end tasmota.add_cron("*/10 * * * * *", ina, "every_10s") #---------------------------------------------- Make the display undim immediately after it has been touched. Status: Tested - works good. -----------------------------------------------# def undim() if (!dimmerstatus) display.dimmer(100) dimmerstatus = true end end tasmota.add_rule("hasp#?#event=up", undim) # MQTT handlers voor switches def handleSwitches(idx, switchinstance) var strvalue = switchinstance.toggle?"On":"Off" switchinstance.text = strvalue mqtt.publish("domoticz/in", json.dump({"command":"switchlight", "idx":idx, "switchcmd":strvalue })) end # Registreer de switch event handlers (pas de idx nummers aan naar jouw Domoticz device IDs) tasmota.add_rule("hasp#p1b72#event=up", / -> handleSwitches(1273, global.p1b72)) # Verlichting Aquarium 1 tasmota.add_rule("hasp#p1b75#event=up", / -> handleSwitches(1287, global.p1b75)) # Filter Aquarium 1 tasmota.add_rule("hasp#p1b78#event=up", / -> handleSwitches(1288, global.p1b78)) # Thermostaat Aquarium 1 tasmota.add_rule("hasp#p1b81#event=up", / -> handleSwitches(1297, global.p1b81)) # CO2 Aquarium 1 tasmota.add_rule("hasp#p1b84#event=up", / -> handleSwitches(415, global.p1b84)) # Temperatuur Aquarium 1 tasmota.add_rule("hasp#p2b72#event=up", / -> handleSwitches(1356, global.p2b72)) # Verlichting Aquarium 2 tasmota.add_rule("hasp#p2b75#event=up", / -> handleSwitches(1586, global.p2b75)) # Filter Aquarium 2 tasmota.add_rule("hasp#p2b78#event=up", / -> handleSwitches(1353, global.p2b78)) # Thermostaat Aquarium 2 tasmota.add_rule("hasp#p2b81#event=up", / -> handleSwitches(1355, global.p2b81)) # CO2 Aquarium 2 tasmota.add_rule("hasp#p2b84#event=up", / -> handleSwitches(447, global.p2b84)) # Temperatuur Aquarium 2 # MQTT subscribers voor updates van Domoticz naar de switches (pas de idx nummers aan naar jouw Domoticz device IDs) def handleUpdates(idx, topic, payload) var p = json.load(payload) if (idx == 1273 && p.contains('svalue1')) # Verlichting global.p1b72.text = p['svalue1'] global.p1b72.toggle = (p['svalue1'] == 'On') global.p1b73.text = p['svalue1'] elif (idx == 1287 && p.contains('svalue1')) # Filter global.p1b75.text = p['svalue1'] global.p1b75.toggle = (p['svalue1'] == 'On') global.p1b76.text = p['svalue1'] elif (idx == 1288 && p.contains('svalue1')) # Thermostaat global.p1b78.text = p['svalue1'] global.p1b78.toggle = (p['svalue1'] == 'On') global.p1b79.text = p['svalue1'] elif (idx == 1297 && p.contains('svalue1')) # CO2 global.p1b81.text = p['svalue1'] global.p1b81.toggle = (p['svalue1'] == 'On') global.p1b82.text = p['svalue1'] elif (idx == 415) # Temperatuursensor print("Temperatuur Aquarium 1: ", payload) var temp1 = number(p['svalue1']) global.p1b84.text = string.format("%.1f °C", temp1) elif (idx == 1356 && p.contains('svalue1')) # Verlichting global.p2b72.text = p['svalue1'] global.p2b72.toggle = (p['svalue1'] == 'On') global.p2b73.text = p['svalue1'] elif (idx == 1586 && p.contains('svalue1')) # Filter global.p2b75.text = p['svalue1'] global.p2b75.toggle = (p['svalue1'] == 'On') global.p2b76.text = p['svalue1'] elif (idx == 1353 && p.contains('svalue1')) # Thermostaat global.p2b78.text = p['svalue1'] global.p2b78.toggle = (p['svalue1'] == 'On') global.p2b79.text = p['svalue1'] elif (idx == 1355 && p.contains('svalue1')) # CO2 Aqua 5 global.p2b81.text = p['svalue1'] global.p2b81.toggle = (p['svalue1'] == 'On') global.p2b82.text = p['svalue1'] elif (idx == 447) # Temperatuursensor Aquarium2 print("Temperatuur Aquarium 2: ", payload) var temp1 = number(p['svalue1']) global.p2b84.text = string.format("%.1f °C", temp1) end end # Abonneer op Domoticz updates (pas de idx nummers aan naar jouw Domoticz device IDs) mqtt.subscribe("domoticz/out/415", /topic, idx, payload, bindata -> handleUpdates(415, topic, payload)) mqtt.subscribe("domoticz/out/447", /topic, idx, payload, bindata -> handleUpdates(447, topic, payload)) mqtt.subscribe("domoticz/out/1273", /topic, idx, payload, bindata -> handleUpdates(1273, topic, payload)) mqtt.subscribe("domoticz/out/1287", /topic, idx, payload, bindata -> handleUpdates(1287, topic, payload)) mqtt.subscribe("domoticz/out/1288", /topic, idx, payload, bindata -> handleUpdates(1288, topic, payload)) mqtt.subscribe("domoticz/out/1297", /topic, idx, payload, bindata -> handleUpdates(1297, topic, payload)) mqtt.subscribe("domoticz/out/1353", /topic, idx, payload, bindata -> handleUpdates(1353, topic, payload)) mqtt.subscribe("domoticz/out/1355", /topic, idx, payload, bindata -> handleUpdates(1355, topic, payload)) mqtt.subscribe("domoticz/out/1356", /topic, idx, payload, bindata -> handleUpdates(1356, topic, payload)) mqtt.subscribe("domoticz/out/1586", /topic, idx, payload, bindata -> handleUpdates(1586, topic, payload)) |
Indien je de juiste IDX-nummers uit Domoticz gebruikt zal je zien dat de switches en temperatuur automatisch bijgewerkt wordt:
Slider dimbare lamp
Als je een slider voor een lamp op je schermpje wil hebben, dan moet je in pages.jsonl onderstaande code toevoegen:
1 2 3 4 5 6 |
[crayon-6899f3205f7a6431589841 inline="1" lang="python" decode="true" ]{"page":3,"comment":"---------- Page 3 Woonkamer ----------"} {"id":0,"text_font":"A:Roboto-Regular.ttf-26","bg_color":"#EAF6FB","bg_grad_dir":0,"text_color":"#101820","bg_opa":255} {"id":70,"obj":"label","text":" Woonkamer","x":0,"y":0,"w":480,"pad_right":90,"h":48,"bg_color":"#2C3EA0","bg_opa":255,"radius":10,"border_side":1,"border_width":0,"border_color":"#FFFFFF","text_font":"A:Roboto-Regular.ttf-34","text_color":"#FFFFFF"} {"id":71,"obj":"label","x":8,"y":80,"w":200,"text":"Lamp woonkamer"} {"id":72,"obj":"slider","x":250,"y":105,"w":150,"h":30,"min":0,"max":100,"val":50,"bg_color":"#B0C4DE","knob_color":"#3B478B","indicator_color":"#7cc2ef"} {"id":73,"obj":"label","x":425,"y":105,"w":60,"h":30,"text":"0%"} |
In het bestand autoexec.be voeg je onderstaande code toe (1140 moet je vervangen voor het IDX van jouw dimbare lamp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
[crayon-6899f3205f7a8704304443 inline="1" lang="python" decode="true" ]# MQTT handler voor slider (dimmer) def handleSlider(idx, sliderobject) var level = sliderobject.val # Verzend naar Domoticz if (level == 0) # Als niveau 0, schakel lamp uit mqtt.publish("domoticz/in", json.dump({ "command": "switchlight", "idx": idx, "switchcmd": "Off" })) global.p3b73.text = "Uit" else # Als niveau > 0, stel dimmer niveau in mqtt.publish("domoticz/in", json.dump({ "command": "switchlight", "idx": idx, "switchcmd": "Set Level", "level": level })) global.p3b73.text = string.format("%d%%", level) end print(string.format("Slider %d ingesteld op niveau: %d", idx, level)) end # Registreer de slider event handler tasmota.add_rule("hasp#p3b72#event=changed", / -> handleSlider(1140, global.p3b72)) # MQTT subscriber voor slider updates van Domoticz def handleSliderUpdates(idx, topic, payload) var p = json.load(payload) if (idx == 1140 && p.contains('svalue1')) # Lamp dimmer IDX 1140 var level = number(p['svalue1']) # Controleer of lamp uit is (niveau 0 of nvalue 0) var is_off = (level == 0) || (p.contains('nvalue') && number(p['nvalue']) == 0) if (is_off) global.p3b72.val = 0 global.p3b73.text = "Uit" else global.p3b72.val = level global.p3b73.text = string.format("%d%%", level) end print(string.format("Slider update ontvangen - niveau: %d, status: %s", level, is_off ? "Uit" : "Aan")) end end # Abonneer op Domoticz updates voor de slider mqtt.subscribe("domoticz/out/1140", /topic, idx, payload, bindata -> handleSliderUpdates(1140, topic, payload)) |
En je hebt een handige slider om de lamp te dimmen:
