annotate web_to_mqtt.py @ 8:47795c3121f1

bufferless updates! mqtt message is sent over SPI and everything works
author drewp@bigasterisk.com
date Mon, 11 Mar 2024 01:37:57 -0700
parents b46679798c51
children affb3c8f3f58
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
1 import asyncio
5
drewp@bigasterisk.com
parents: 4
diff changeset
2 import itertools
drewp@bigasterisk.com
parents: 4
diff changeset
3 import random
drewp@bigasterisk.com
parents: 4
diff changeset
4 import struct
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
5 import subprocess
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
6 import tempfile
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
7
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
8 import aiomqtt
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
9 from PIL import Image, ImageChops
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
10
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
11
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
12 class WebRenderer:
5
drewp@bigasterisk.com
parents: 4
diff changeset
13
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
14 def __init__(self):
5
drewp@bigasterisk.com
parents: 4
diff changeset
15 self.chrome_proc = subprocess.Popen(["google-chrome", "--headless"])
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
16 print("Chrome subprocess started.")
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
17
5
drewp@bigasterisk.com
parents: 4
diff changeset
18 async def capture_screenshot(self, url) -> Image.Image:
drewp@bigasterisk.com
parents: 4
diff changeset
19 out = tempfile.NamedTemporaryFile(suffix=".png", prefix='webrenderer_')
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
20 screenshot_command = [
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
21 "google-chrome",
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
22 "--headless",
8
47795c3121f1 bufferless updates! mqtt message is sent over SPI and everything works
drewp@bigasterisk.com
parents: 7
diff changeset
23 "--window-size=320,480",
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
24 f"--screenshot={out.name}",
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
25 url,
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
26 ]
5
drewp@bigasterisk.com
parents: 4
diff changeset
27 subprocess.run(screenshot_command,
drewp@bigasterisk.com
parents: 4
diff changeset
28 stdout=subprocess.DEVNULL,
drewp@bigasterisk.com
parents: 4
diff changeset
29 stderr=subprocess.DEVNULL)
drewp@bigasterisk.com
parents: 4
diff changeset
30 return Image.open(out.name).convert('RGB')
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
31
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
32
7
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
33 blockX = 32
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
34 blockY = 32
8
47795c3121f1 bufferless updates! mqtt message is sent over SPI and everything works
drewp@bigasterisk.com
parents: 7
diff changeset
35 msgDelay = .05
5
drewp@bigasterisk.com
parents: 4
diff changeset
36 dirtyQueue = {}
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
37
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
38
5
drewp@bigasterisk.com
parents: 4
diff changeset
39 async def check_for_changes(renderer, client, last_image):
drewp@bigasterisk.com
parents: 4
diff changeset
40 current_image = await renderer.capture_screenshot(
drewp@bigasterisk.com
parents: 4
diff changeset
41 "http://localhost:8002/front-door-display/scheduleLcd.html")
drewp@bigasterisk.com
parents: 4
diff changeset
42 diff_image = ImageChops.difference(last_image, current_image)
6
e36abecb48a1 experimenting with block size & delay
drewp@bigasterisk.com
parents: 5
diff changeset
43 for y in range(0, current_image.height, blockY):
e36abecb48a1 experimenting with block size & delay
drewp@bigasterisk.com
parents: 5
diff changeset
44 for x in range(0, current_image.width, blockX):
e36abecb48a1 experimenting with block size & delay
drewp@bigasterisk.com
parents: 5
diff changeset
45 box = (x, y, x + blockX, y + blockY)
5
drewp@bigasterisk.com
parents: 4
diff changeset
46 region = diff_image.crop(box)
drewp@bigasterisk.com
parents: 4
diff changeset
47 if region.getbbox():
drewp@bigasterisk.com
parents: 4
diff changeset
48 dirtyQueue[(x, y)] = current_image.crop(box)
drewp@bigasterisk.com
parents: 4
diff changeset
49 await asyncio.sleep(0)
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
50
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
51 return current_image
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
52
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
53
5
drewp@bigasterisk.com
parents: 4
diff changeset
54 async def sendDirty(client):
drewp@bigasterisk.com
parents: 4
diff changeset
55 while True:
drewp@bigasterisk.com
parents: 4
diff changeset
56 if dirtyQueue:
drewp@bigasterisk.com
parents: 4
diff changeset
57 # pos = random.choice(list(dirtyQueue.keys()))
8
47795c3121f1 bufferless updates! mqtt message is sent over SPI and everything works
drewp@bigasterisk.com
parents: 7
diff changeset
58 pos = min(list(dirtyQueue.keys()))
5
drewp@bigasterisk.com
parents: 4
diff changeset
59 img = dirtyQueue.pop(pos)
drewp@bigasterisk.com
parents: 4
diff changeset
60 await tell_lcd(client, pos[0], pos[1], img)
7
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
61 await asyncio.sleep(msgDelay) # too fast and esp restarts
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
62
5
drewp@bigasterisk.com
parents: 4
diff changeset
63
drewp@bigasterisk.com
parents: 4
diff changeset
64 framesSent = itertools.count()
drewp@bigasterisk.com
parents: 4
diff changeset
65
drewp@bigasterisk.com
parents: 4
diff changeset
66
drewp@bigasterisk.com
parents: 4
diff changeset
67 async def tell_lcd(client: aiomqtt.Client, x: int, y: int,
drewp@bigasterisk.com
parents: 4
diff changeset
68 region: Image.Image):
6
e36abecb48a1 experimenting with block size & delay
drewp@bigasterisk.com
parents: 5
diff changeset
69 seq = next(framesSent) % 65535
7
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
70 msg = struct.pack('HHHHH', seq, x, y, region.width,
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
71 region.height) + region.tobytes()
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
72 print(f'send {seq=} {x=} {y=} {region.width=} {region.height=} ',
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
73 end='\r',
b46679798c51 mv esp code to this repo. still trying to get correct refreshes
drewp@bigasterisk.com
parents: 6
diff changeset
74 flush=True)
5
drewp@bigasterisk.com
parents: 4
diff changeset
75 await client.publish('display/squib/updates', msg, qos=0)
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
76
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
77
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
78 async def main():
5
drewp@bigasterisk.com
parents: 4
diff changeset
79 # also listen for dirty msgs from the web page; see ts.
drewp@bigasterisk.com
parents: 4
diff changeset
80 # also get notified of new mqtt listeners who need a full image refresh.
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
81 renderer = WebRenderer()
5
drewp@bigasterisk.com
parents: 4
diff changeset
82 async with aiomqtt.Client("mqtt2") as client:
drewp@bigasterisk.com
parents: 4
diff changeset
83 asyncio.create_task(sendDirty(client))
8
47795c3121f1 bufferless updates! mqtt message is sent over SPI and everything works
drewp@bigasterisk.com
parents: 7
diff changeset
84 last_image = Image.new('RGB', (320, 480))
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
85 while True:
5
drewp@bigasterisk.com
parents: 4
diff changeset
86 last_image = await check_for_changes(renderer, client, last_image)
drewp@bigasterisk.com
parents: 4
diff changeset
87 # we could get the web page to tell us when any dom changes
8
47795c3121f1 bufferless updates! mqtt message is sent over SPI and everything works
drewp@bigasterisk.com
parents: 7
diff changeset
88 await asyncio.sleep(1)
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
89
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
90
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
91 if __name__ == "__main__":
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
92 asyncio.run(main())