AriW
Published

WithPlants: A Handheld Plants Explorer

To connect with plants. Capture, sense, and listen to plants with a pocket-sized device that makes every walk an adventure.

BeginnerWork in progress1 hour180
WithPlants: A Handheld Plants Explorer

Things used in this project

Hardware components

M5Stack CoreS3 Lite ESP32S3 IoT Dev Kit
M5Stack CoreS3 Lite ESP32S3 IoT Dev Kit
×1
ENV IV 
M5Stack ENV IV 
×1

Software apps and online services

M5Stack UIFlow 2.0

Story

Read more

Code

Plant Code

Python
import os, time, struct
import M5
from M5 import *
from M5 import Mic, Speaker
from machine import Pin, I2C, SoftI2C
from unit import ENVUnit
import m5ui
import lvgl as lv
import camera

MOCK_PLANT_NAME = "Camphor Tree"
MOCK_CONFIDENCE = 87.2

current_page = "nav"
pages = {}

btn_photo = None
btn_env = None
btn_audio = None
lab_Loading = None

btn_take = None
back_btn_capture = None
lab_capture = None
lbl_connection = None

btn_retake = None
lbl_meet = None
lbl_plant = None
back_btn_result = None

lbl_temp = None
lbl_humi = None
lbl_pres = None
button9 = None
button10 = None
label13 = None

button11 = None
btn_play = None
lbl_audio = None
lbl_volume = None
switch_record = None
button12 = None
button14 = None

camera_initialized = False
recording = False
volume = 50
rec_data = None
record_start_time = 0
last_capture_img = None

ENV_AVAILABLE = False
AUDIO_AVAILABLE = False
_env_unit = None
_i2c = None

env_update_timer = None
recording_check_timer = None
playback_check_timer = None

def switch_page(page_name):
    """页面切换核心函数"""
    global current_page, env_update_timer, lbl_connection, last_capture_img
    
    print("Attempting to switch to page: {}".format(page_name))
    
    if current_page == "env" and env_update_timer:
        env_update_timer.delete()
        env_update_timer = None
    
    if current_page == "audio":
        reset_audio_state()
    
    try:
        print("Pages available: {}".format(list(pages.keys())))
        if page_name in pages:
            print("Page {} exists, value: {}".format(page_name, pages[page_name]))
        else:
            print("Page {} not in pages dictionary".format(page_name))
        
        if page_name in pages and pages[page_name]:
            current_page = page_name
            pages[page_name].screen_load()
            print("Successfully switched to page: {}".format(page_name))
            
            if page_name == "env":
                show_env_data_once()
            elif page_name == "photo_capture":
                if lbl_connection:
                    if not camera_initialized:
                        lbl_connection.set_text("Connecting...")
                        lv.timer_create(init_camera_async, 100, None)
                    else:
                        lbl_connection.set_text("Camera ready")
            elif page_name == "photo_result":
                if last_capture_img:
                    lv.timer_create(lambda t: (t.delete(), display_photo(last_capture_img)), 50, None)
            elif page_name == "audio":
                if lbl_audio:
                    lbl_audio.set_text("Swipe to record")
        else:
            print("ERROR: Page not found or is None: {}".format(page_name))
            print("Available pages: {}".format([(k, v is not None) for k, v in pages.items()]))
            if page_name == "photo_result":
                print("Attempting to recreate photo_result page...")
                pages["photo_result"] = build_photo_result_page()
                if pages["photo_result"]:
                    print("Successfully recreated photo_result page")
                    switch_page(page_name)
                else:
                    print("Failed to recreate photo_result page")
    except Exception as e:
        print("Error switching page: {}".format(e))
        import sys
        sys.print_exception(e)

def init_camera_async(timer):
    """异步初始化相机"""
    global lbl_connection
    timer.delete()
    
    if camera_init():
        if lbl_connection:
            lbl_connection.set_text("Camera ready")
    else:
        if lbl_connection:
            lbl_connection.set_text("Camera init failed")

def camera_init():
    """初始化相机"""
    global camera_initialized
    try:
        camera.init(pixformat=camera.RGB565, framesize=camera.QVGA)
        camera_initialized = True
        print("Camera initialized successfully")
        return True
    except Exception as e:
        camera_initialized = False
        print("Camera init failed: {}".format(e))
        return False

def camera_deinit():
    """释放相机资源"""
    global camera_initialized
    try:
        camera.deinit()
        camera_initialized = False
        print("Camera deinitialized")
    except:
        pass

def capture_photo():
    """拍照函数"""
    global last_capture_img
    try:
        if not camera_initialized:
            if not camera_init():
                return None
        
        img = camera.snapshot()
        if img:
            last_capture_img = img
            print("Photo captured successfully")
            return img
        else:
            print("Capture failed - no image")
            return None
    except Exception as e:
        print("Capture error: {}".format(e))
        return None

def display_photo(img):
    """显示照片在结果页面"""
    try:
        M5.Lcd.show(img, 6, 35, 310, 139)
        return True
    except Exception as e:
        print("Display photo error: {}".format(e))
        return False

def on_photo_click(evt):
    """导航页面 - 相机按钮点击"""
    if evt.code == lv.EVENT.RELEASED:
        print("Photo button clicked")
        switch_page("photo_capture")

def on_env_click(evt):
    """导航页面 - ENV按钮点击"""
    if evt.code == lv.EVENT.RELEASED:
        print("ENV button clicked")
        switch_page("env")

def on_audio_click(evt):
    """导航页面 - 音频按钮点击"""
    if evt.code == lv.EVENT.RELEASED:
        print("Audio button clicked")
        switch_page("audio")

def on_back_click(evt):
    """返回按钮点击 - 返回导航页"""
    if evt.code == lv.EVENT.RELEASED:
        print("Back button clicked")
        switch_page("nav")

def on_take_photo(evt):
    """拍照按钮点击事件"""
    global lbl_connection
    if evt.code == lv.EVENT.RELEASED:
        print("Take photo clicked")
        if lbl_connection:
            lbl_connection.set_text("Taking photo...")
        lv.timer_create(do_capture_async, 10, None)

def do_capture_async(timer):
    """拍照异步处理"""
    global last_capture_img, lbl_connection
    timer.delete()
    
    img = capture_photo()
    if img:
        print("Photo captured, switching to result page...")
        switch_page("photo_result")
        if lbl_connection:
            lbl_connection.set_text("Photo captured!")
    else:
        if lbl_connection:
            lbl_connection.set_text("Capture failed")

def on_retake_photo(evt):
    """重拍按钮点击事件"""
    if evt.code == lv.EVENT.RELEASED:
        print("Retake photo clicked")
        switch_page("photo_capture")

def on_reset_click(evt):
    """ENV重置按钮点击"""
    if evt.code == lv.EVENT.RELEASED:
        print("Reset button clicked")
        if lbl_temp:
            lbl_temp.set_text("Temperature: Reading...")
        if lbl_humi:
            lbl_humi.set_text("Humidity: Reading...")
        if lbl_pres:
            lbl_pres.set_text("Pressure: Reading...")
        lv.timer_create(reset_env_async, 100, None)

def reset_env_async(timer):
    timer.delete()
    if reset_env_sensor():
        show_env_data_once()
    else:
        if lbl_temp:
            lbl_temp.set_text("Temperature: Reset Failed")
        if lbl_humi:
            lbl_humi.set_text("Humidity: Reset Failed")
        if lbl_pres:
            lbl_pres.set_text("Pressure: Reset Failed")

def on_record_switch(evt):
    """录音开关切换"""
    global switch_record
    if evt.code == lv.EVENT.VALUE_CHANGED:
        if switch_record and switch_record.get_state():
            print("Record switch ON")
            audio_record()
        else:
            print("Record switch OFF")
            if recording:
                stop_recording()

def on_play_click(evt):
    """播放按钮点击"""
    if evt.code == lv.EVENT.RELEASED:
        print("Play button clicked")
        audio_play()

def build_nav_page():
    """构建导航主页"""
    global btn_photo, btn_env, btn_audio, lab_Loading
    
    try:
        print("Building nav page...")
        page = m5ui.M5Page(bg_c=0xecece7)
        
        btn_photo = m5ui.M5Button(text="  MEET NEW PLANTS  ", x=17, y=19, 
                                 bg_c=0xecece7, text_c=0x7dcc79, 
                                 font=lv.font_montserrat_24, parent=page)
        
        btn_env = m5ui.M5Button(text="HABITAT OF PLANTS", x=17, y=81, 
                               bg_c=0xecece7, text_c=0x7dcc79, 
                               font=lv.font_montserrat_24, parent=page)

        btn_audio = m5ui.M5Button(text=" LISTEN TO PLANTS  ", x=20, y=145, 
                                 bg_c=0xecece7, text_c=0x7dcc79, 
                                 font=lv.font_montserrat_24, parent=page)
        
        lab_Loading = m5ui.M5Label("YOUR PLANTS' FRIEND", x=82, y=214, 
                                  text_c=0xececec, bg_c=0xc0d36a, bg_opa=255, 
                                  font=lv.font_montserrat_14, parent=page)
        
        btn_photo.add_event_cb(on_photo_click, lv.EVENT.RELEASED, None)
        btn_env.add_event_cb(on_env_click, lv.EVENT.RELEASED, None)
        btn_audio.add_event_cb(on_audio_click, lv.EVENT.RELEASED, None)
        
        print("Nav page built successfully")
        return page
        
    except Exception as e:
        print("Error building nav page: {}".format(e))
        import sys
        sys.print_exception(e)
        return None

def build_photo_capture_page():
    global btn_take, back_btn_capture, lab_capture, lbl_connection
    
    try:
        print("Building photo capture page...")
        page = m5ui.M5Page(bg_c=0xecece7)
        
        back_btn_capture = m5ui.M5Button(text="BACK", x=10, y=10,
                                         bg_c=0xecece7, text_c=0x383838, 
                                         font=lv.font_montserrat_14, parent=page)
        back_btn_capture.add_event_cb(on_back_click, lv.EVENT.RELEASED, None)
        
        btn_take = m5ui.M5Button(text="             CAPTURE!               ", x=0, y=100,
                                bg_c=0x7dcc79, text_c=0xecece7, 
                                font=lv.font_montserrat_24, parent=page)
        btn_take.add_event_cb(on_take_photo, lv.EVENT.RELEASED, None)
        
        lbl_connection = m5ui.M5Label("Connecting...", x=100, y=215, 
                                     text_c=0x7dcc79, bg_c=0xecece7, bg_opa=0, 
                                     font=lv.font_montserrat_16, parent=page)
        
        print("Photo capture page built successfully")
        return page
        
    except Exception as e:
        print("Error building photo capture page: {}".format(e))
        import sys
        sys.print_exception(e)
        return None

def build_photo_result_page():
    """构建相机结果页面 - 简化版本"""
    global btn_retake, lbl_meet, lbl_plant, back_btn_result
    
    try:
        print("Building photo result page...")
        print("MOCK_PLANT_NAME: {}, MOCK_CONFIDENCE: {}".format(MOCK_PLANT_NAME, MOCK_CONFIDENCE))
        
        page = m5ui.M5Page(bg_c=0xecece7)
        print("Created page object")
        
        back_btn_result = m5ui.M5Button(text="BACK", x=10, y=5, 
                                       w=55, h=25,
                                       bg_c=0xecece7, text_c=0x383838, 
                                       font=lv.font_montserrat_14, parent=page)
        back_btn_result.add_event_cb(on_back_click, lv.EVENT.RELEASED, None)
        print("Created back button")
        
        btn_retake = m5ui.M5Button(text="RETAKE", x=250, y=5,
                                  w=65, h=25, 
                                  bg_c=0xecece7, text_c=0x383838, 
                                  font=lv.font_montserrat_14, parent=page)
        btn_retake.add_event_cb(on_retake_photo, lv.EVENT.RELEASED, None)
        print("Created retake button")
        
        lbl_meet = m5ui.M5Label("You meet the", x=6, y=180, 
                               text_c=0x000000, bg_c=0xecece7, bg_opa=0, 
                               font=lv.font_montserrat_24, parent=page)
        print("Created meet label")
        
        plant_text = "{}! {}%".format(MOCK_PLANT_NAME, MOCK_CONFIDENCE)
        print("Plant text: {}".format(plant_text))
        
        lbl_plant = m5ui.M5Label(plant_text, 
                                x=6, y=209, 
                                text_c=0x000000, bg_c=0xecece7, bg_opa=0, 
                                font=lv.font_montserrat_24, parent=page)
        print("Created plant label")
        
        print("Photo result page built successfully")
        return page
        
    except Exception as e:
        print("DETAILED ERROR building photo result page: {}".format(e))
        import sys
        sys.print_exception(e)
        return None

def build_env_page():
    """构建环境传感器页面"""
    global button9, button10, lbl_temp, lbl_humi, lbl_pres, label13
    
    try:
        print("Building env page...")
        page = m5ui.M5Page(bg_c=0xecece7)
        
        button9 = m5ui.M5Button(text="BACK", x=-1, y=0, bg_c=0xecece7, 
                               text_c=0x383838, font=lv.font_montserrat_14, parent=page)
        button9.add_event_cb(on_back_click, lv.EVENT.RELEASED, None)
        
        button10 = m5ui.M5Button(text="RESET", x=242, y=0, bg_c=0xecece7, 
                                text_c=0x383838, font=lv.font_montserrat_14, parent=page)
        button10.add_event_cb(on_reset_click, lv.EVENT.RELEASED, None)
        
        label13 = m5ui.M5Label("Habitat of Plants", x=18, y=48, text_c=0x9e9e9e, 
                              bg_c=0xfff27f, bg_opa=0, 
                              font=lv.font_montserrat_24, parent=page)
        
        lbl_temp = m5ui.M5Label("Temperature:26.9 C", x=19, y=92, text_c=0x000000, 
                               bg_c=0xfff27f, bg_opa=255, 
                               font=lv.font_montserrat_24, parent=page)
        
        lbl_humi = m5ui.M5Label("Humidity: 50.6%", x=18, y=140, text_c=0x000000, 
                               bg_c=0x7dcc79, bg_opa=255, 
                               font=lv.font_montserrat_24, parent=page)
        
        lbl_pres = m5ui.M5Label("Pressure: 1007.4hPa", x=20, y=190, text_c=0x000000, 
                               bg_c=0x69afee, bg_opa=255, 
                               font=lv.font_montserrat_24, parent=page)
        
        print("Env page built successfully")
        return page
        
    except Exception as e:
        print("Error building env page: {}".format(e))
        import sys
        sys.print_exception(e)
        return None

def build_audio_page():
    """构建音频页面"""
    global button11, btn_play, lbl_audio, lbl_volume, switch_record, button12, button14, volume
    
    try:
        print("Building audio page...")
        page = m5ui.M5Page(bg_c=0xecece7)
        
        button11 = m5ui.M5Button(text="Back", x=0, y=0, bg_c=0xecece7, 
                                text_c=0x404040, font=lv.font_montserrat_14, parent=page)
        button11.add_event_cb(on_back_click, lv.EVENT.RELEASED, None)

        lbl_audio = m5ui.M5Label("Swipe to record", x=105, y=33, text_c=0x9e9e9e, 
                                bg_c=0xffffff, bg_opa=0, 
                                font=lv.font_montserrat_14, parent=page)
        
        switch_record = m5ui.M5Switch(x=21, y=61, w=274, h=96, bg_c=0xe2e2e2, 
                                      bg_c_checked=0x7dcc79, circle_c=0x93d990, parent=page)
        switch_record.add_event_cb(on_record_switch, lv.EVENT.VALUE_CHANGED, None)
        
        btn_play = m5ui.M5Button(text="  PLAY              ", x=-9, y=195, 
                                bg_c=0xea89a9, text_c=0x000000, 
                                font=lv.font_montserrat_24, parent=page)
        btn_play.add_event_cb(on_play_click, lv.EVENT.RELEASED, None)
        
        lbl_volume = m5ui.M5Label("VOL: 50%", x=242, y=172, text_c=0x000000, 
                                 bg_c=0xffffff, bg_opa=0, 
                                 font=lv.font_montserrat_14, parent=page)
        
        button14 = m5ui.M5Button(text="-", x=232, y=195, bg_c=0xe2e2e2, 
                                text_c=0x488be0, font=lv.font_montserrat_24, parent=page)
        
        button12 = m5ui.M5Button(text="+", x=274, y=194, bg_c=0xe2e2e2, 
                                text_c=0xf65050, font=lv.font_montserrat_24, parent=page)
        
        def on_vol_down(evt):
            global volume, lbl_volume
            if evt.code == lv.EVENT.RELEASED:
                volume = max(0, volume - 10)
                if lbl_volume:
                    lbl_volume.set_text("VOL: {}%".format(volume))
                print("Volume: {}%".format(volume))
        
        def on_vol_up(evt):
            global volume, lbl_volume
            if evt.code == lv.EVENT.RELEASED:
                volume = min(100, volume + 10)
                if lbl_volume:
                    lbl_volume.set_text("VOL: {}%".format(volume))
                print("Volume: {}%".format(volume))
        
        button14.add_event_cb(on_vol_down, lv.EVENT.RELEASED, None)
        button12.add_event_cb(on_vol_up, lv.EVENT.RELEASED, None)
        
        print("Audio page built successfully")
        return page
        
    except Exception as e:
        print("Error building audio page: {}".format(e))
        import sys
        sys.print_exception(e)
        return None

def env_init():
    """初始化ENV-IV模块"""
    global ENV_AVAILABLE, _env_unit, _i2c
    
    try:
        print("Initializing ENV-IV on PORT.A (SCL=1, SDA=2)...")
        
        try:
            _i2c = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000)
            print("Using hardware I2C")
        except:
            _i2c = SoftI2C(scl=Pin(1), sda=Pin(2), freq=100000)
            print("Using software I2C")
        
        devices = _i2c.scan()
        print("I2C devices found: {}".format([hex(d) for d in devices]))
        
        if not devices:
            print("No I2C devices found - ENV-IV not connected")
            ENV_AVAILABLE = False
            return False
        
        try:
            from unit import ENVUnit
            _env_unit = ENVUnit(i2c=_i2c, type=4)
            
            temp = _env_unit.read_temperature()
            humi = _env_unit.read_humidity()
            pres = _env_unit.read_pressure()
            
            ENV_AVAILABLE = True
            print("ENV-IV initialized successfully")
            print("Initial reading: T={:.1f}°C, H={:.1f}%, P={:.1f}hPa".format(temp, humi, pres))
            return True
            
        except Exception as e:
            print("ENVUnit(type=4) failed: {}".format(str(e)[:50]))
            ENV_AVAILABLE = False
            _env_unit = None
            return False
            
    except Exception as e:
        ENV_AVAILABLE = False
        _env_unit = None
        _i2c = None
        print("ENV initialization error: {}".format(e))
        return False

def show_env_data_once():
    """读取并显示ENV传感器数据"""
    global lbl_temp, lbl_humi, lbl_pres, _env_unit, ENV_AVAILABLE
    
    if not lbl_temp or not lbl_humi or not lbl_pres:
        return
    
    if not ENV_AVAILABLE or not _env_unit:
        lbl_temp.set_text("Temperature: No Sensor")
        lbl_humi.set_text("Humidity: No Sensor")
        lbl_pres.set_text("Pressure: No Sensor")
        return
    
    print("Reading ENV sensors...")
    
    try:
        time.sleep_ms(100)
        temperature = _env_unit.read_temperature()
        lbl_temp.set_text("Temperature: {:.1f}°C".format(temperature))
        print("Temperature: {:.1f}°C".format(temperature))
    except Exception as e:
        lbl_temp.set_text("Temperature: Failed")
        print("Temperature read failed: {}".format(str(e)[:20]))
    
    try:
        time.sleep_ms(100)
        humidity = _env_unit.read_humidity()
        lbl_humi.set_text("Humidity: {:.1f}%".format(humidity))
        print("Humidity: {:.1f}%".format(humidity))
    except Exception as e:
        lbl_humi.set_text("Humidity: Failed")
        print("Humidity read failed: {}".format(str(e)[:20]))
    
    try:
        time.sleep_ms(100)
        pressure = _env_unit.read_pressure()
        lbl_pres.set_text("Pressure: {:.1f}hPa".format(pressure))
        print("Pressure: {:.1f}hPa".format(pressure))
    except Exception as e:
        lbl_pres.set_text("Pressure: Failed")
        print("Pressure read failed: {}".format(str(e)[:20]))

def reset_env_sensor():
    """重置ENV传感器"""
    global ENV_AVAILABLE, _env_unit, _i2c
    
    print("Resetting ENV sensor...")
    
    ENV_AVAILABLE = False
    _env_unit = None
    
    try:
        _i2c = SoftI2C(scl=Pin(1), sda=Pin(2), freq=50000)
        time.sleep_ms(300)
        
        from unit import ENVUnit
        _env_unit = ENVUnit(i2c=_i2c, type=4)
        
        time.sleep_ms(500)
        temp = _env_unit.read_temperature()
        
        ENV_AVAILABLE = True
        print("ENV sensor reset successful: T={:.1f}°C".format(temp))
        return True
        
    except Exception as e:
        print("ENV sensor reset failed: {}".format(e))
        ENV_AVAILABLE = False
        _env_unit = None
        return False

def audio_init():
    """初始化音频系统"""
    global AUDIO_AVAILABLE, rec_data, volume
    try:
        rec_data = bytearray(8000 * 25)
        volume = 50
        
        try:
            Speaker.begin()
            Speaker.setVolumePercentage(volume)
            Speaker.end()
            print("Speaker initialized successfully")
        except Exception as e:
            print("Speaker init warning: {}".format(e))
        
        AUDIO_AVAILABLE = True
        print("Audio initialized - 25s buffer, volume: {}%".format(volume))
    except Exception as e:
        AUDIO_AVAILABLE = False
        rec_data = None
        volume = 50
        print("Audio init failed: {}".format(e))

def reset_audio_state():
    """重置音频系统状态"""
    global recording, playback_check_timer, recording_check_timer, switch_record
    
    try:
        if recording:
            try:
                Mic.end()
            except:
                pass
            recording = False
        
        if switch_record:
            try:
                switch_record.set_state(False)
            except:
                pass
        
        try:
            Speaker.end()
        except:
            pass
        
        if playback_check_timer:
            playback_check_timer.delete()
            playback_check_timer = None
        
        if recording_check_timer:
            recording_check_timer.delete()
            recording_check_timer = None
        
        print("Audio state reset")
        
    except Exception as e:
        print("Reset audio error: {}".format(e))

def stop_recording():
    """停止录音"""
    global recording, recording_check_timer, switch_record
    
    if recording:
        try:
            Mic.end()
        except:
            pass
        recording = False
        
        if switch_record:
            try:
                switch_record.set_state(False)
            except:
                pass
        
        if lbl_audio:
            lbl_audio.set_text("Recording stopped")
        if recording_check_timer:
            recording_check_timer.delete()
            recording_check_timer = None
        print("Recording stopped manually")

def audio_record():
    """开始录音"""
    global recording, rec_data, record_start_time, recording_check_timer
    
    if not AUDIO_AVAILABLE or rec_data is None:
        if lbl_audio:
            lbl_audio.set_text("Audio not available")
        return
    
    if recording:
        if lbl_audio:
            lbl_audio.set_text("Already recording...")
        return
    
    try:
        if lbl_audio:
            lbl_audio.set_text("Starting recording...")
        
        try:
            Mic.end()
        except:
            pass
        
        Mic.begin()
        time.sleep_ms(100)
        
        Mic.record(rec_data, 8000, True)
        
        recording = True
        record_start_time = time.ticks_ms()
        
        if lbl_audio:
            lbl_audio.set_text("Recording... 0/20s")
        
        if recording_check_timer:
            recording_check_timer.delete()
            recording_check_timer = None
        
        recording_check_timer = lv.timer_create(check_recording_status_async, 200, None)
        
        print("Recording started with 25s buffer")
        
    except Exception as e:
        if lbl_audio:
            lbl_audio.set_text("Record error: {}".format(str(e)[:20]))
        recording = False
        print("Recording error: {}".format(e))
        try:
            Mic.end()
        except:
            pass

def check_recording_status_async(timer):
    """检查录音状态"""
    global recording, record_start_time, recording_check_timer, switch_record
    
    if not recording:
        if recording_check_timer:
            recording_check_timer.delete()
            recording_check_timer = None
        return
    
    try:
        if record_start_time:
            elapsed = time.ticks_diff(time.ticks_ms(), record_start_time) // 1000
            if elapsed < 20:
                if lbl_audio:
                    lbl_audio.set_text("Recording... {}/20s".format(elapsed))
            elif elapsed >= 20:
                print("20 seconds reached, stopping recording...")
                try:
                    Mic.end()
                except:
                    pass
                recording = False
                
                if switch_record:
                    try:
                        switch_record.set_state(False)
                    except:
                        pass
                
                if lbl_audio:
                    lbl_audio.set_text("Recording complete (20s)!")
                if recording_check_timer:
                    recording_check_timer.delete()
                    recording_check_timer = None
                return
        
        try:
            if not Mic.isRecording():
                elapsed = time.ticks_diff(time.ticks_ms(), record_start_time) // 1000 if record_start_time else 0
                try:
                    Mic.end()
                except:
                    pass
                recording = False
                
                if switch_record:
                    try:
                        switch_record.set_state(False)
                    except:
                        pass
                
                if lbl_audio:
                    lbl_audio.set_text("Recording done ({}s)!".format(elapsed))
                print("Recording completed - {} seconds".format(elapsed))
                
                if recording_check_timer:
                    recording_check_timer.delete()
                    recording_check_timer = None
        except Exception as e:
            print("Recording status check error: {}".format(e))
            
    except Exception as e:
        print("Recording check error: {}".format(e))
        recording = False
        if recording_check_timer:
            recording_check_timer.delete()
            recording_check_timer = None

def audio_play():
    """播放录音"""
    global rec_data, volume, playback_check_timer
    
    if not AUDIO_AVAILABLE or rec_data is None:
        if lbl_audio:
            lbl_audio.set_text("No audio data")
        return
    
    if recording:
        if lbl_audio:
            lbl_audio.set_text("Stop recording first")
        return
    
    try:
        if lbl_audio:
            lbl_audio.set_text("Starting playback...")
        
        Speaker.begin()
        Speaker.setVolumePercentage(volume)
        time.sleep_ms(50)
        
        Speaker.playRaw(rec_data, 8000)
        
        if lbl_audio:
            lbl_audio.set_text("Playing...")
        
        print("Playback started - volume: {}%".format(volume))
        
        if playback_check_timer:
            playback_check_timer.delete()
            playback_check_timer = None
        
        playback_check_timer = lv.timer_create(check_playback_status_async, 300, None)
        
    except Exception as e:
        if lbl_audio:
            lbl_audio.set_text("Play error: {}".format(str(e)[:20]))
        print("Playback error: {}".format(e))
        try:
            Speaker.end()
        except:
            pass

def check_playback_status_async(timer):
    """检查播放状态"""
    global playback_check_timer
    
    try:
        is_playing = False
        try:
            is_playing = Speaker.isPlaying()
        except:
            is_playing = False
        
        if not is_playing:
            if lbl_audio:
                current_text = lbl_audio.get_text()
                if "Playing..." in current_text:
                    lbl_audio.set_text("Playback done!")
                    print("Playback completed")
            
            try:
                Speaker.end()
            except:
                pass
            
            if playback_check_timer:
                playback_check_timer.delete()
                playback_check_timer = None
                
    except Exception as e:
        print("Playback check error: {}".format(e))
        if playback_check_timer:
            playback_check_timer.delete()
            playback_check_timer = None

def setup_ui():
    """UI系统初始化"""
    global pages
    
    print("Setting up UI...")
    
    try:
        print("Creating nav page...")
        pages["nav"] = build_nav_page()
        print("Nav page result: {}".format(pages["nav"] is not None))
        
        print("Creating photo_capture page...")
        pages["photo_capture"] = build_photo_capture_page()
        print("Photo capture page result: {}".format(pages["photo_capture"] is not None))
        
        print("Creating photo_result page...")
        pages["photo_result"] = build_photo_result_page()
        print("Photo result page result: {}".format(pages["photo_result"] is not None))
        
        print("Creating env page...")
        pages["env"] = build_env_page()
        print("Env page result: {}".format(pages["env"] is not None))
        
        print("Creating audio page...")
        pages["audio"] = build_audio_page()
        print("Audio page result: {}".format(pages["audio"] is not None))
        
        print("Final page status:")
        for page_name, page_obj in pages.items():
            print("  {}: {}".format(page_name, "OK" if page_obj else "FAILED"))
        
        if pages["nav"]:
            switch_page("nav")
            print("UI setup complete")
        else:
            print("CRITICAL: Nav page failed!")
            
    except Exception as e:
        print("Exception in setup_ui: {}".format(e))
        import sys
        sys.print_exception(e)

def main():
    """主程序入口"""
    print("Starting CoreS3 Plant ID System...")
    
    try:
        M5.begin()
        print("M5 initialized")
        
        Widgets.setRotation(1)
        
        m5ui.init()
        print("m5ui initialized")
        
        setup_ui()
        
        env_init()
        audio_init()
        
        print("System ready!")
        print("Entering main loop...")
        
        while True:
            M5.update()
            time.sleep_ms(5)
            
    except Exception as e:
        print("Main error: {}".format(e))
        import sys
        sys.print_exception(e)
        camera_deinit()
        raise

if __name__ == "__main__":
    main()

Credits

AriW
1 project • 0 followers

Comments