You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Remove .micropython/lib symlink workaround
This commit is contained in:
@@ -123,12 +123,6 @@ or
|
||||
~/sources/MicroPythonOS/scripts/build_lvgl_micropython.sh macOS
|
||||
```
|
||||
|
||||
To run it, it's recommended to symlink your ~/.micropython/lib folder into this project's lib:
|
||||
|
||||
```
|
||||
ln -sf $(readlink -f internal_filesystem/lib) ~/.micropython/lib
|
||||
```
|
||||
|
||||
Then to run it, do:
|
||||
|
||||
```
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/home/user/sources/PiggyOS/internal_filesystem/apps/com.lightningpiggy.displaywallet/assets/captureqr.py
|
||||
@@ -0,0 +1,277 @@
|
||||
# This code grabs images from the camera in RGB565 format (2 bytes per pixel)
|
||||
# and sends that to the QR decoder if QR decoding is enabled.
|
||||
# The QR decoder then converts the RGB565 to grayscale, as that's what quirc operates on.
|
||||
# It would be slightly more efficient to capture the images from the camera in L8/grayscale format,
|
||||
# or in YUV format and discarding the U and V planes, but then the image will be gray (not great UX)
|
||||
# and the performance impact of converting RGB565 to grayscale is probably minimal anyway.
|
||||
|
||||
import lvgl as lv
|
||||
|
||||
try:
|
||||
import webcam
|
||||
except Exception as e:
|
||||
print(f"Info: could not import webcam module: {e}")
|
||||
|
||||
from mpos.apps import Activity
|
||||
|
||||
class Camera(Activity):
|
||||
|
||||
width = 240
|
||||
height = 240
|
||||
|
||||
status_label_text = "No camera found."
|
||||
status_label_text_searching = "Searching QR codes...\n\nHold still and make them big!\n10cm for simple QR codes,\n20cm for complex."
|
||||
status_label_text_found = "Decoding QR..."
|
||||
|
||||
cam = None
|
||||
current_cam_buffer = None # Holds the current memoryview to prevent garbage collection
|
||||
|
||||
image = None
|
||||
image_dsc = None
|
||||
scanqr_mode = None
|
||||
use_webcam = False
|
||||
keepliveqrdecoding = False
|
||||
|
||||
capture_timer = None
|
||||
|
||||
# Widgets:
|
||||
qr_label = None
|
||||
qr_button = None
|
||||
snap_button = None
|
||||
status_label = None
|
||||
status_label_cont = None
|
||||
|
||||
def onCreate(self):
|
||||
self.scanqr_mode = self.getIntent().extras.get("scanqr_mode")
|
||||
main_screen = lv.obj()
|
||||
main_screen.set_style_pad_all(0, 0)
|
||||
main_screen.set_style_border_width(0, 0)
|
||||
main_screen.set_size(lv.pct(100), lv.pct(100))
|
||||
main_screen.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
|
||||
close_button = lv.button(main_screen)
|
||||
close_button.set_size(60,60)
|
||||
close_button.align(lv.ALIGN.TOP_RIGHT, 0, 0)
|
||||
close_label = lv.label(close_button)
|
||||
close_label.set_text(lv.SYMBOL.CLOSE)
|
||||
close_label.center()
|
||||
close_button.add_event_cb(lambda e: self.finish(),lv.EVENT.CLICKED,None)
|
||||
self.snap_button = lv.button(main_screen)
|
||||
self.snap_button.set_size(60, 60)
|
||||
self.snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0)
|
||||
self.snap_button.add_flag(lv.obj.FLAG.HIDDEN)
|
||||
self.snap_button.add_event_cb(self.snap_button_click,lv.EVENT.CLICKED,None)
|
||||
snap_label = lv.label(self.snap_button)
|
||||
snap_label.set_text(lv.SYMBOL.OK)
|
||||
snap_label.center()
|
||||
self.qr_button = lv.button(main_screen)
|
||||
self.qr_button.set_size(60, 60)
|
||||
self.qr_button.add_flag(lv.obj.FLAG.HIDDEN)
|
||||
self.qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0)
|
||||
self.qr_button.add_event_cb(self.qr_button_click,lv.EVENT.CLICKED,None)
|
||||
self.qr_label = lv.label(self.qr_button)
|
||||
self.qr_label.set_text(lv.SYMBOL.EYE_OPEN)
|
||||
self.qr_label.center()
|
||||
# Initialize LVGL image widget
|
||||
self.image = lv.image(main_screen)
|
||||
self.image.align(lv.ALIGN.LEFT_MID, 0, 0)
|
||||
# Create image descriptor once
|
||||
self.image_dsc = lv.image_dsc_t({
|
||||
"header": {
|
||||
"magic": lv.IMAGE_HEADER_MAGIC,
|
||||
"w": self.width,
|
||||
"h": self.height,
|
||||
"stride": self.width * 2,
|
||||
"cf": lv.COLOR_FORMAT.RGB565
|
||||
#"cf": lv.COLOR_FORMAT.L8
|
||||
},
|
||||
'data_size': self.width * self.height * 2,
|
||||
'data': None # Will be updated per frame
|
||||
})
|
||||
self.image.set_src(self.image_dsc)
|
||||
self.status_label_cont = lv.obj(main_screen)
|
||||
self.status_label_cont.set_size(lv.pct(66),lv.pct(60))
|
||||
self.status_label_cont.align(lv.ALIGN.LEFT_MID, lv.pct(5), 0)
|
||||
self.status_label_cont.set_style_bg_color(lv.color_white(), 0)
|
||||
self.status_label_cont.set_style_bg_opa(66, 0)
|
||||
self.status_label_cont.set_style_border_width(0, 0)
|
||||
self.status_label = lv.label(self.status_label_cont)
|
||||
self.status_label.set_text("No camera found.")
|
||||
self.status_label.set_long_mode(lv.label.LONG.WRAP)
|
||||
self.status_label.set_style_text_color(lv.color_white(), 0)
|
||||
self.status_label.set_width(lv.pct(100))
|
||||
self.status_label.center()
|
||||
self.setContentView(main_screen)
|
||||
|
||||
def onResume(self, screen):
|
||||
self.cam = init_internal_cam()
|
||||
if self.cam:
|
||||
self.image.set_rotation(900) # internal camera is rotated 90 degrees
|
||||
else:
|
||||
print("camtest.py: no internal camera found, trying webcam on /dev/video0")
|
||||
try:
|
||||
self.cam = webcam.init("/dev/video0")
|
||||
self.use_webcam = True
|
||||
except Exception as e:
|
||||
print(f"camtest.py: webcam exception: {e}")
|
||||
if self.cam:
|
||||
print("Camera initialized, continuing...")
|
||||
self.capture_timer = lv.timer_create(self.try_capture, 100, None)
|
||||
self.status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
|
||||
if self.scanqr_mode:
|
||||
self.start_qr_decoding()
|
||||
else:
|
||||
self.qr_button.remove_flag(lv.obj.FLAG.HIDDEN)
|
||||
self.snap_button.remove_flag(lv.obj.FLAG.HIDDEN)
|
||||
else:
|
||||
print("No camera found, stopping camtest.py")
|
||||
if self.scanqr_mode:
|
||||
self.finish()
|
||||
|
||||
|
||||
def onStop(self, screen):
|
||||
print("camtest.py backgrounded, cleaning up...")
|
||||
if self.capture_timer:
|
||||
self.capture_timer.delete()
|
||||
if self.use_webcam:
|
||||
webcam.deinit(self.cam)
|
||||
elif self.cam:
|
||||
self.cam.deinit()
|
||||
print("camtest.py cleanup done.")
|
||||
|
||||
def qrdecode_one(self):
|
||||
try:
|
||||
import qrdecode
|
||||
result = qrdecode.qrdecode_rgb565(self.current_cam_buffer, self.width, self.height)
|
||||
#result = bytearray("INSERT_QR_HERE", "utf-8")
|
||||
if not result:
|
||||
self.status_label.set_text(self.status_label_text_searching)
|
||||
else:
|
||||
self.stop_qr_decoding()
|
||||
result = remove_bom(result)
|
||||
result = print_qr_buffer(result)
|
||||
print(f"QR decoding found: {result}")
|
||||
if self.scanqr_mode:
|
||||
self.setResult(True, result)
|
||||
self.finish()
|
||||
else:
|
||||
self.status_label.set_text(result) # in the future, the status_label text should be copy-paste-able
|
||||
except ValueError as e:
|
||||
print("QR ValueError: ", e)
|
||||
self.status_label.set_text(self.status_label_text_searching)
|
||||
except TypeError as e:
|
||||
print("QR TypeError: ", e)
|
||||
self.status_label.set_text(self.status_label_text_found)
|
||||
except Exception as e:
|
||||
print("QR got other error: ", e)
|
||||
|
||||
def snap_button_click(self, e):
|
||||
print("Picture taken!")
|
||||
import os
|
||||
try:
|
||||
os.mkdir("data")
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
os.mkdir("data/com.example.camtest")
|
||||
except OSError:
|
||||
pass
|
||||
if self.current_cam_buffer is not None:
|
||||
filename="data/com.example.camtest/capture.raw"
|
||||
try:
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(self.current_cam_buffer)
|
||||
print(f"Successfully wrote current_cam_buffer to {filename}")
|
||||
except OSError as e:
|
||||
print(f"Error writing to file: {e}")
|
||||
|
||||
def start_qr_decoding(self):
|
||||
print("Activating live QR decoding...")
|
||||
self.keepliveqrdecoding = True
|
||||
self.qr_label.set_text(lv.SYMBOL.EYE_CLOSE)
|
||||
self.status_label_cont.remove_flag(lv.obj.FLAG.HIDDEN)
|
||||
self.status_label.set_text(self.status_label_text_searching)
|
||||
|
||||
def stop_qr_decoding(self):
|
||||
print("Deactivating live QR decoding...")
|
||||
self.keepliveqrdecoding = False
|
||||
self.qr_label.set_text(lv.SYMBOL.EYE_OPEN)
|
||||
self.status_label_text = self.status_label.get_text()
|
||||
if self.status_label_text in (self.status_label_text_searching or self.status_label_text_found): # if it found a QR code, leave it
|
||||
self.status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
|
||||
|
||||
def qr_button_click(self, e):
|
||||
if not self.keepliveqrdecoding:
|
||||
self.start_qr_decoding()
|
||||
else:
|
||||
self.stop_qr_decoding()
|
||||
|
||||
def try_capture(self, event):
|
||||
#print("capturing camera frame")
|
||||
try:
|
||||
if self.use_webcam:
|
||||
self.current_cam_buffer = webcam.capture_frame(self.cam, "rgb565")
|
||||
elif self.cam.frame_available():
|
||||
self.current_cam_buffer = self.cam.capture()
|
||||
if self.current_cam_buffer and len(self.current_cam_buffer):
|
||||
self.image_dsc.data = self.current_cam_buffer
|
||||
#image.invalidate() # does not work so do this:
|
||||
self.image.set_src(self.image_dsc)
|
||||
if not self.use_webcam:
|
||||
self.cam.free_buffer() # Free the old buffer
|
||||
if self.keepliveqrdecoding:
|
||||
self.qrdecode_one()
|
||||
except Exception as e:
|
||||
print(f"Camera capture exception: {e}")
|
||||
|
||||
|
||||
|
||||
# Non-class functions:
|
||||
def init_internal_cam():
|
||||
try:
|
||||
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
|
||||
cam = Camera(
|
||||
data_pins=[12,13,15,11,14,10,7,2],
|
||||
vsync_pin=6,
|
||||
href_pin=4,
|
||||
sda_pin=21,
|
||||
scl_pin=16,
|
||||
pclk_pin=9,
|
||||
xclk_pin=8,
|
||||
xclk_freq=20000000,
|
||||
powerdown_pin=-1,
|
||||
reset_pin=-1,
|
||||
pixel_format=PixelFormat.RGB565,
|
||||
#pixel_format=PixelFormat.GRAYSCALE,
|
||||
frame_size=FrameSize.R240X240,
|
||||
grab_mode=GrabMode.LATEST
|
||||
)
|
||||
#cam.init() automatically done when creating the Camera()
|
||||
#cam.reconfigure(frame_size=FrameSize.HVGA)
|
||||
#frame_size=FrameSize.HVGA, # 480x320
|
||||
#frame_size=FrameSize.QVGA, # 320x240
|
||||
#frame_size=FrameSize.QQVGA # 160x120
|
||||
cam.set_vflip(True)
|
||||
return cam
|
||||
except Exception as e:
|
||||
print(f"init_cam exception: {e}")
|
||||
return None
|
||||
|
||||
def print_qr_buffer(buffer):
|
||||
try:
|
||||
# Try to decode buffer as a UTF-8 string
|
||||
result = buffer.decode('utf-8')
|
||||
# Check if the string is printable (ASCII printable characters)
|
||||
if all(32 <= ord(c) <= 126 for c in result):
|
||||
return result
|
||||
except Exception as e:
|
||||
pass
|
||||
# If not a valid string or not printable, convert to hex
|
||||
hex_str = ' '.join([f'{b:02x}' for b in buffer])
|
||||
return hex_str.lower()
|
||||
|
||||
# Byte-Order-Mark is added sometimes
|
||||
def remove_bom(buffer):
|
||||
bom = b'\xEF\xBB\xBF'
|
||||
if buffer.startswith(bom):
|
||||
return buffer[3:]
|
||||
return buffer
|
||||
@@ -4,6 +4,10 @@ import lcd_bus
|
||||
import lvgl as lv
|
||||
import sdl_display
|
||||
|
||||
# Add lib/ to the path for modules, otherwise it will only search in ~/.micropython/lib and /usr/lib/micropython
|
||||
import sys
|
||||
sys.path.append('lib/')
|
||||
|
||||
import mpos.ui
|
||||
|
||||
#TFT_HOR_RES=640
|
||||
@@ -32,5 +36,6 @@ mouse = sdl_pointer.SDLPointer()
|
||||
|
||||
#keyboard.add_event_cb(keyboard_cb, lv.EVENT.ALL, None)
|
||||
|
||||
|
||||
print("boot_unix.py finished")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user