Skip to content

Commit 6fa179d

Browse files
task_handler needs to be called from the same thread...
1 parent e6d1607 commit 6fa179d

File tree

7 files changed

+405
-1
lines changed

7 files changed

+405
-1
lines changed

draft_code/image_tests.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
width=240
2+
height=240
3+
4+
import webcam
5+
import time
6+
7+
cam = webcam.init("/dev/video0") # Initialize webcam with device path
8+
memview = webcam.capture_frame(cam) # Returns memoryview
9+
time.sleep_ms(1000)
10+
static_bytes_obj = bytes(memview)
11+
12+
13+
image = lv.image(lv.screen_active())
14+
image.align(lv.ALIGN.LEFT_MID, 0, 0)
15+
image.set_rotation(900)
16+
# Create image descriptor once
17+
image_dsc = lv.image_dsc_t({
18+
"header": {
19+
"magic": lv.IMAGE_HEADER_MAGIC,
20+
"w": width,
21+
"h": height,
22+
"stride": width ,
23+
"cf": lv.COLOR_FORMAT.L8
24+
},
25+
'data_size': width * height,
26+
'data': static_bytes_obj # Will be updated per frame
27+
})
28+
image.set_src(image_dsc)
29+
30+
for i in range(300):
31+
print(f"iteration {i}")
32+
webcam.recapture_frame(cam) #refresh memview
33+
bytes_obj = bytes(memview)
34+
#print(f"got bytes: {len(bytes_obj)}")
35+
#image_dsc.data = static_bytes_obj
36+
image_dsc.data = bytes_obj
37+
#image.set_src(image_dsc)
38+
image.invalidate()
39+
time.sleep_ms(10) # seems to need more than 0 or 1 ms
40+
41+
print("cleanup")
42+
webcam.deinit(cam) # Deinitializes webcam
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "CamTestNew",
3+
"publisher": "ACME Inc",
4+
"short_description": "Simple test of the camera",
5+
"long_description": "A simple test of the camera makes it possible to validate the hardware.",
6+
"icon_url": "http://demo.lnpiggy.com:2121/apps/com.example.camtest_0.0.2.mpk_icon_64x64.png",
7+
"download_url": "http://demo.lnpiggy.com:2121/apps/com.example.camtest_0.0.2.mpk",
8+
"fullname": "com.example.camtest",
9+
"version": "0.0.2",
10+
"entrypoint": "assets/camtestnew.py",
11+
"category": "camera"
12+
}
13+
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
import time
2+
import webcam
3+
4+
appscreen = lv.screen_active()
5+
6+
keepgoing = True
7+
keepliveqrdecoding = False
8+
width = 240
9+
height = 240
10+
11+
# Variable to hold the current memoryview to prevent garbage collection
12+
current_cam_buffer = None
13+
image_dsc = None
14+
image = None
15+
qr_label = None
16+
use_webcam = False
17+
18+
19+
def print_qr_buffer(buffer):
20+
try:
21+
# Try to decode buffer as a UTF-8 string
22+
result = buffer.decode('utf-8')
23+
# Check if the string is printable (ASCII printable characters)
24+
if all(32 <= ord(c) <= 126 for c in result):
25+
return result
26+
except Exception as e:
27+
pass
28+
# If not a valid string or not printable, convert to hex
29+
hex_str = ' '.join([f'{b:02x}' for b in buffer])
30+
return hex_str.lower()
31+
32+
# Byte-Order-Mark is added sometimes
33+
def remove_bom(buffer):
34+
bom = b'\xEF\xBB\xBF'
35+
if buffer.startswith(bom):
36+
return buffer[3:]
37+
return buffer
38+
39+
def qrdecode_live():
40+
# Image dimensions
41+
buffer_size = width * height # 240 * 240 = 57600 bytes
42+
while keepgoing and keepliveqrdecoding:
43+
try:
44+
import qrdecode
45+
result = qrdecode.qrdecode(current_cam_buffer, width, height)
46+
result = remove_bom(result)
47+
result = print_qr_buffer(result)
48+
print(f"QR decoding found: {result}")
49+
except Exception as e:
50+
print("QR decode error: ", e)
51+
time.sleep_ms(500)
52+
53+
54+
def close_button_click(e):
55+
global keepgoing
56+
print("Close button clicked")
57+
keepgoing = False
58+
59+
60+
def snap_button_click(e):
61+
print("Picture taken!")
62+
try:
63+
import os
64+
os.mkdir("data")
65+
os.mkdir("data/com.example.camtest")
66+
except OSError:
67+
pass
68+
if current_cam_buffer is not None:
69+
filename="data/com.example.camtest/capture.raw"
70+
try:
71+
with open(filename, 'wb') as f:
72+
f.write(current_cam_buffer)
73+
print(f"Successfully wrote current_cam_buffer to {filename}")
74+
except OSError as e:
75+
print(f"Error writing to file: {e}")
76+
77+
78+
def qr_button_click(e):
79+
global keepliveqrdecoding, qr_label
80+
if not keepliveqrdecoding:
81+
print("Activating live QR decoding...")
82+
keepliveqrdecoding = True
83+
qr_label.set_text(lv.SYMBOL.EYE_CLOSE)
84+
try:
85+
import _thread
86+
_thread.stack_size(12*1024) # 16KB is too much
87+
_thread.start_new_thread(qrdecode_live, ())
88+
except Exception as e:
89+
print("Could not start live QR decoding thread: ", e)
90+
else:
91+
print("Deactivating live QR decoding...")
92+
keepliveqrdecoding = False
93+
qr_label.set_text(lv.SYMBOL.EYE_OPEN)
94+
95+
def try_capture():
96+
global current_cam_buffer, image_dsc, image
97+
use_webcam = True # Set to True for webcam module
98+
if use_webcam:
99+
new_cam_buffer = None
100+
new_cam_buffer = webcam.capture_frame(cam) # Returns bytes
101+
else:
102+
new_cam_buffer = cam.capture() # Returns memoryview for other camera
103+
if new_cam_buffer and len(new_cam_buffer) == 240 * 240:
104+
if current_cam_buffer is not None:
105+
if use_webcam:
106+
#pass
107+
webcam.free_buffer(cam) # Explicitly free webcam buffer
108+
else:
109+
cam.free_buffer() # Free other camera buffer
110+
else:
111+
pass
112+
print("current_cam_buffer is None, not freeing...")
113+
current_cam_buffer = None
114+
current_cam_buffer = new_cam_buffer # Store new buffer reference
115+
# Update image descriptor with new buffer
116+
# Set image source to update LVGL
117+
#image_dsc.data = None # this doesnt help
118+
#image.set_src(None) #this crashes it
119+
oldsrc = image.get_src()
120+
if oldsrc:
121+
oldsrc.delete()
122+
#image_dsc.data = current_cam_buffer
123+
image_dsc = lv.image_dsc_t({
124+
"header": {
125+
"magic": lv.IMAGE_HEADER_MAGIC,
126+
"w": width,
127+
"h": height,
128+
"stride": width ,
129+
#"cf": lv.COLOR_FORMAT.RGB565
130+
"cf": lv.COLOR_FORMAT.L8
131+
},
132+
'data_size': width * height,
133+
'data': current_cam_buffer # Will be updated per frame
134+
})
135+
#image.invalidate()
136+
image.set_src(image_dsc)
137+
else:
138+
print("Invalid buffer size:", len(new_cam_buffer))
139+
if use_webcam:
140+
pass
141+
#webcam.free_buffer(cam)
142+
else:
143+
cam.free_buffer()
144+
145+
def try_capture_again():
146+
global current_cam_buffer, image_dsc, image
147+
new_cam_buffer = webcam.capture_frame(cam) # Returns memoryview
148+
if new_cam_buffer and len(new_cam_buffer) == 240 * 240:
149+
image_dsc.data = new_cam_buffer # Update image descriptor
150+
image.set_src(image_dsc) # Update LVGL image
151+
current_cam_buffer = None # Clear reference to allow GC
152+
else:
153+
print("Invalid buffer size:", len(new_cam_buffer))
154+
155+
def try_capture_old():
156+
global current_cam_buffer, image_dsc, image, use_webcam
157+
if use_webcam:
158+
new_cam_buffer = webcam.capture_frame(cam)
159+
elif cam.frame_available():
160+
new_cam_buffer = cam.capture() # Returns memoryview
161+
if new_cam_buffer and len(new_cam_buffer):
162+
# print("Invalid buffer size:", len(new_cam_buffer))
163+
# cam.free_buffer()
164+
# return
165+
# Update image descriptor with new memoryview
166+
image_dsc.data = new_cam_buffer
167+
# Set image source to update LVGL (implicitly invalidates widget)
168+
image.set_src(image_dsc)
169+
#current_cam_buffer = None # Clear reference to allow GC
170+
#image.invalidate() #does not work
171+
# Free the previous buffer (if any) after setting new data
172+
if current_cam_buffer is not None and not use_webcam:
173+
cam.free_buffer() # Free the old buffer
174+
current_cam_buffer = new_cam_buffer # Store new buffer reference
175+
176+
177+
def build_ui():
178+
global image, image_dsc,qr_label
179+
cont = lv.obj(appscreen)
180+
cont.set_style_pad_all(0, 0)
181+
cont.set_style_border_width(0, 0)
182+
cont.set_size(lv.pct(100), lv.pct(100))
183+
cont.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
184+
close_button = lv.button(cont)
185+
close_button.set_size(60,60)
186+
close_button.align(lv.ALIGN.TOP_RIGHT, 0, 0)
187+
close_label = lv.label(close_button)
188+
close_label.set_text(lv.SYMBOL.CLOSE)
189+
close_label.center()
190+
close_button.add_event_cb(close_button_click,lv.EVENT.CLICKED,None)
191+
snap_button = lv.button(cont)
192+
snap_button.set_size(60, 60)
193+
snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0)
194+
snap_label = lv.label(snap_button)
195+
snap_label.set_text(lv.SYMBOL.OK)
196+
snap_label.center()
197+
snap_button.add_event_cb(snap_button_click,lv.EVENT.CLICKED,None)
198+
qr_button = lv.button(cont)
199+
qr_button.set_size(60, 60)
200+
qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0)
201+
qr_label = lv.label(qr_button)
202+
qr_label.set_text(lv.SYMBOL.EYE_OPEN)
203+
qr_label.center()
204+
qr_button.add_event_cb(qr_button_click,lv.EVENT.CLICKED,None)
205+
# Initialize LVGL image widget
206+
image = lv.image(cont)
207+
image.align(lv.ALIGN.LEFT_MID, 0, 0)
208+
image.set_rotation(900)
209+
# Create image descriptor once
210+
image_dsc = lv.image_dsc_t({
211+
"header": {
212+
"magic": lv.IMAGE_HEADER_MAGIC,
213+
"w": width,
214+
"h": height,
215+
"stride": width ,
216+
#"cf": lv.COLOR_FORMAT.RGB565
217+
"cf": lv.COLOR_FORMAT.L8
218+
},
219+
'data_size': width * height,
220+
'data': None # Will be updated per frame
221+
})
222+
image.set_src(image_dsc)
223+
224+
225+
def init_cam():
226+
try:
227+
# time.sleep(1) doesn't help
228+
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
229+
cam = Camera(
230+
data_pins=[12,13,15,11,14,10,7,2],
231+
vsync_pin=6,
232+
href_pin=4,
233+
sda_pin=21,
234+
scl_pin=16,
235+
pclk_pin=9,
236+
xclk_pin=8,
237+
xclk_freq=20000000,
238+
powerdown_pin=-1,
239+
reset_pin=-1,
240+
#pixel_format=PixelFormat.RGB565,
241+
pixel_format=PixelFormat.GRAYSCALE,
242+
frame_size=FrameSize.R240X240,
243+
grab_mode=GrabMode.LATEST
244+
)
245+
#cam.init() automatically done when creating the Camera()
246+
#cam.reconfigure(frame_size=FrameSize.HVGA)
247+
#frame_size=FrameSize.HVGA, # 480x320
248+
#frame_size=FrameSize.QVGA, # 320x240
249+
#frame_size=FrameSize.QQVGA # 160x120
250+
cam.set_vflip(True)
251+
return cam
252+
except Exception as e:
253+
print(f"init_cam exception: {e}")
254+
return None
255+
256+
257+
258+
259+
260+
261+
cam = init_cam()
262+
if not cam:
263+
print("init cam failed, retrying with webcam...")
264+
try:
265+
cam = webcam.init("/dev/video0") # Initialize webcam with device path
266+
use_webcam = True
267+
except Exception as e:
268+
print(f"camtest.py: webcam exception: {e}")
269+
270+
if cam or use_webcam:
271+
build_ui()
272+
count=0
273+
while appscreen == lv.screen_active() and keepgoing is True:
274+
print(f"capture nr {count}")
275+
count += 1
276+
try_capture()
277+
time.sleep_ms(100) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS.
278+
print("App backgrounded, deinitializing camera...")
279+
if use_webcam:
280+
webcam.deinit(cam) # Deinitializes webcam
281+
else:
282+
cam.deinit()
283+
else:
284+
print("No camera found, exiting...")
285+
286+
show_launcher()
287+
288+

0 commit comments

Comments
 (0)