99
1010# Variable to hold the current memoryview to prevent garbage collection
1111current_cam_buffer = None
12+ image_dsc = None
13+ image = None
14+ qr_label = None
1215
1316
14- cont = lv .obj (appscreen )
15- cont .set_style_pad_all (0 , 0 )
16- cont .set_style_border_width (0 , 0 )
17- cont .set_size (lv .pct (100 ), lv .pct (100 ))
18- cont .set_scrollbar_mode (lv .SCROLLBAR_MODE .OFF )
19-
20- snap_button = lv .button (cont )
21- snap_button .set_size (60 , 60 )
22- snap_button .align (lv .ALIGN .RIGHT_MID , 0 , 0 )
23- snap_label = lv .label (snap_button )
24- snap_label .set_text (lv .SYMBOL .OK )
25- snap_label .center ()
26-
27- def snap_button_click (e ):
28- print ("Picture taken!" )
29- try :
30- import os
31- os .mkdir ("data" )
32- os .mkdir ("data/com.example.camtest" )
33- except OSError :
34- pass
35- if current_cam_buffer is not None :
36- filename = "data/com.example.camtest/capture.raw"
37- try :
38- with open (filename , 'wb' ) as f :
39- f .write (current_cam_buffer )
40- print (f"Successfully wrote current_cam_buffer to { filename } " )
41- except OSError as e :
42- print (f"Error writing to file: { e } " )
43-
44- snap_button .add_event_cb (snap_button_click ,lv .EVENT .CLICKED ,None )
45-
46-
47- qr_button = lv .button (cont )
48- qr_button .set_size (60 , 60 )
49- qr_button .align (lv .ALIGN .BOTTOM_RIGHT , 0 , 0 )
50- qr_label = lv .label (qr_button )
51- qr_label .set_text (lv .SYMBOL .EYE_OPEN )
52- qr_label .center ()
53-
54- def process_qr_buffer (buffer ):
17+ def print_qr_buffer (buffer ):
5518 try :
5619 # Try to decode buffer as a UTF-8 string
5720 result = buffer .decode ('utf-8' )
@@ -64,21 +27,52 @@ def process_qr_buffer(buffer):
6427 hex_str = ' ' .join ([f'{ b :02x} ' for b in buffer ])
6528 return hex_str .lower ()
6629
30+ # Byte-Order-Mark is added sometimes
31+ def remove_bom (buffer ):
32+ bom = b'\xEF \xBB \xBF '
33+ if buffer .startswith (bom ):
34+ return buffer [3 :]
35+ return buffer
36+
6737def qrdecode_live ():
6838 # Image dimensions
6939 buffer_size = width * height # 240 * 240 = 57600 bytes
7040 while keepgoing and keepliveqrdecoding :
7141 try :
7242 import qrdecode
7343 result = qrdecode .qrdecode (current_cam_buffer , width , height )
74- if result .startswith ('\ufeff ' ): # Remove BOM (\ufeff) from the start of the decoded string, if present
75- result = result [1 :]
76- result = process_qr_buffer (result )
44+ result = remove_bom (result )
45+ result = print_qr_buffer (result )
7746 print (f"QR decoding found: { result } " )
7847 except Exception as e :
7948 print ("QR decode error: " , e )
8049 time .sleep_ms (500 )
8150
51+
52+ def close_button_click (e ):
53+ global keepgoing
54+ print ("Close button clicked" )
55+ keepgoing = False
56+
57+
58+ def snap_button_click (e ):
59+ print ("Picture taken!" )
60+ try :
61+ import os
62+ os .mkdir ("data" )
63+ os .mkdir ("data/com.example.camtest" )
64+ except OSError :
65+ pass
66+ if current_cam_buffer is not None :
67+ filename = "data/com.example.camtest/capture.raw"
68+ try :
69+ with open (filename , 'wb' ) as f :
70+ f .write (current_cam_buffer )
71+ print (f"Successfully wrote current_cam_buffer to { filename } " )
72+ except OSError as e :
73+ print (f"Error writing to file: { e } " )
74+
75+
8276def qr_button_click (e ):
8377 global keepliveqrdecoding , qr_label
8478 if not keepliveqrdecoding :
@@ -96,26 +90,80 @@ def qr_button_click(e):
9690 keepliveqrdecoding = False
9791 qr_label .set_text (lv .SYMBOL .EYE_OPEN )
9892
99- qr_button .add_event_cb (qr_button_click ,lv .EVENT .CLICKED ,None )
10093
94+ def try_capture ():
95+ global current_cam_buffer , image_dsc , image
96+ if cam .frame_available ():
97+ # Get new memoryview from camera
98+ new_cam_buffer = cam .capture () # Returns memoryview
99+ # Verify buffer size
100+ #if len(new_cam_buffer) != width * height * 2:
101+ # print("Invalid buffer size:", len(new_cam_buffer))
102+ # cam.free_buffer()
103+ # return
104+ # Update image descriptor with new memoryview
105+ image_dsc .data = new_cam_buffer
106+ # Set image source to update LVGL (implicitly invalidates widget)
107+ image .set_src (image_dsc )
108+ #image.invalidate() #does not work
109+ # Free the previous buffer (if any) after setting new data
110+ if current_cam_buffer is not None :
111+ cam .free_buffer () # Free the old buffer
112+ current_cam_buffer = new_cam_buffer # Store new buffer reference
101113
102- close_button = lv .button (cont )
103- close_button .set_size (60 ,60 )
104- close_button .align (lv .ALIGN .TOP_RIGHT , 0 , 0 )
105- close_label = lv .label (close_button )
106- close_label .set_text (lv .SYMBOL .CLOSE )
107- close_label .center ()
108- def close_button_click (e ):
109- global keepgoing
110- print ("Close button clicked" )
111- keepgoing = False
112114
113- close_button .add_event_cb (close_button_click ,lv .EVENT .CLICKED ,None )
114115
116+ def build_ui ():
117+ global image , image_dsc ,qr_label
118+ cont = lv .obj (appscreen )
119+ cont .set_style_pad_all (0 , 0 )
120+ cont .set_style_border_width (0 , 0 )
121+ cont .set_size (lv .pct (100 ), lv .pct (100 ))
122+ cont .set_scrollbar_mode (lv .SCROLLBAR_MODE .OFF )
123+ close_button = lv .button (cont )
124+ close_button .set_size (60 ,60 )
125+ close_button .align (lv .ALIGN .TOP_RIGHT , 0 , 0 )
126+ close_label = lv .label (close_button )
127+ close_label .set_text (lv .SYMBOL .CLOSE )
128+ close_label .center ()
129+ close_button .add_event_cb (close_button_click ,lv .EVENT .CLICKED ,None )
130+ snap_button = lv .button (cont )
131+ snap_button .set_size (60 , 60 )
132+ snap_button .align (lv .ALIGN .RIGHT_MID , 0 , 0 )
133+ snap_label = lv .label (snap_button )
134+ snap_label .set_text (lv .SYMBOL .OK )
135+ snap_label .center ()
136+ snap_button .add_event_cb (snap_button_click ,lv .EVENT .CLICKED ,None )
137+ qr_button = lv .button (cont )
138+ qr_button .set_size (60 , 60 )
139+ qr_button .align (lv .ALIGN .BOTTOM_RIGHT , 0 , 0 )
140+ qr_label = lv .label (qr_button )
141+ qr_label .set_text (lv .SYMBOL .EYE_OPEN )
142+ qr_label .center ()
143+ qr_button .add_event_cb (qr_button_click ,lv .EVENT .CLICKED ,None )
144+ # Initialize LVGL image widget
145+ image = lv .image (cont )
146+ image .align (lv .ALIGN .LEFT_MID , 0 , 0 )
147+ image .set_rotation (900 )
148+ # Create image descriptor once
149+ image_dsc = lv .image_dsc_t ({
150+ "header" : {
151+ "magic" : lv .IMAGE_HEADER_MAGIC ,
152+ "w" : width ,
153+ "h" : height ,
154+ "stride" : width ,
155+ #"cf": lv.COLOR_FORMAT.RGB565
156+ "cf" : lv .COLOR_FORMAT .L8
157+ },
158+ 'data_size' : width * height ,
159+ 'data' : None # Will be updated per frame
160+ })
161+ image .set_src (image_dsc )
115162
116- from camera import Camera , GrabMode , PixelFormat , FrameSize , GainCeiling
117163
118164try :
165+ # time.sleep(1) doesn't help
166+ from camera import Camera , GrabMode , PixelFormat , FrameSize , GainCeiling
119167 cam = Camera (
120168 data_pins = [12 ,13 ,15 ,11 ,14 ,10 ,7 ,2 ],
121169 vsync_pin = 6 ,
@@ -133,69 +181,19 @@ def close_button_click(e):
133181 grab_mode = GrabMode .LATEST
134182 )
135183 #cam.init() automatically done when creating the Camera()
184+ #cam.reconfigure(frame_size=FrameSize.HVGA)
185+ #frame_size=FrameSize.HVGA, # 480x320
186+ #frame_size=FrameSize.QVGA, # 320x240
187+ #frame_size=FrameSize.QQVGA # 160x120
188+ cam .set_vflip (True )
189+ build_ui ()
190+ while appscreen == lv .screen_active () and keepgoing is True :
191+ try_capture ()
192+ time .sleep_ms (100 ) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS.
193+ print ("App backgrounded, deinitializing camera..." )
194+ cam .deinit ()
195+ show_launcher ()
136196except Exception as e :
137- print (f"Exception while initializing camera: { e } " )
138-
139- #cam.reconfigure(frame_size=FrameSize.HVGA)
140- #frame_size=FrameSize.HVGA, # 480x320
141- #frame_size=FrameSize.QVGA, # 320x240
142- #frame_size=FrameSize.QQVGA # 160x120
143-
144- cam .set_vflip (True )
145-
146-
147- # Initialize LVGL image widget
148- image = lv .image (cont )
149- image .align (lv .ALIGN .LEFT_MID , 0 , 0 )
150- image .set_rotation (900 )
151-
152- # Create image descriptor once
153- image_dsc = lv .image_dsc_t ({
154- "header" : {
155- "magic" : lv .IMAGE_HEADER_MAGIC ,
156- "w" : width ,
157- "h" : height ,
158- "stride" : width ,
159- #"cf": lv.COLOR_FORMAT.RGB565
160- "cf" : lv .COLOR_FORMAT .L8
161- },
162- 'data_size' : width * height ,
163- 'data' : None # Will be updated per frame
164- })
165-
166- # Set initial image source (optional, can be set in try_capture)
167- image .set_src (image_dsc )
168-
169-
170- def try_capture ():
171- global current_cam_buffer
172- if cam .frame_available ():
173- # Get new memoryview from camera
174- new_cam_buffer = cam .capture () # Returns memoryview
175- # Verify buffer size
176- #if len(new_cam_buffer) != width * height * 2:
177- # print("Invalid buffer size:", len(new_cam_buffer))
178- # cam.free_buffer()
179- # return
180- # Update image descriptor with new memoryview
181- image_dsc .data = new_cam_buffer
182- # Set image source to update LVGL (implicitly invalidates widget)
183- image .set_src (image_dsc )
184- #image.invalidate() #does not work
185- # Free the previous buffer (if any) after setting new data
186- if current_cam_buffer is not None :
187- cam .free_buffer () # Free the old buffer
188- current_cam_buffer = new_cam_buffer # Store new buffer reference
189-
190- # Initial capture
191- try_capture ()
192-
193-
194- while appscreen == lv .screen_active () and keepgoing is True :
195- try_capture ()
196- time .sleep_ms (100 ) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS.
197+ print (f"Exception: { e } " )
197198
198- print ("App backgrounded, deinitializing camera..." )
199- cam .deinit ()
200199
201- show_launcher ()
0 commit comments