finish
This commit is contained in:
59
display.py
59
display.py
@@ -210,3 +210,62 @@ class Display:
|
||||
|
||||
self.tft.line(x, y + 5, x + 3, y + 8, st7789.GREEN)
|
||||
self.tft.line(x + 3, y + 8, x + 10, y, st7789.GREEN)
|
||||
|
||||
def render_confirm_screen(self, asr_text=""):
|
||||
"""渲染确认界面"""
|
||||
if not self.tft:
|
||||
return
|
||||
|
||||
self.tft.fill(st7789.BLACK)
|
||||
|
||||
# Header
|
||||
self.tft.fill_rect(0, 0, 240, 30, st7789.CYAN)
|
||||
self.text("说完了吗?", 75, 8, st7789.BLACK)
|
||||
|
||||
# Content box
|
||||
self.tft.fill_rect(10, 50, 220, 90, 0x4208) # DARKGREY
|
||||
|
||||
if asr_text:
|
||||
# 自动换行逻辑
|
||||
max_width = 200
|
||||
lines = []
|
||||
current_line = ""
|
||||
current_width = 0
|
||||
|
||||
for char in asr_text:
|
||||
char_width = 16 if ord(char) > 127 else 8
|
||||
if current_width + char_width > max_width:
|
||||
lines.append(current_line)
|
||||
current_line = char
|
||||
current_width = char_width
|
||||
else:
|
||||
current_line += char
|
||||
current_width += char_width
|
||||
|
||||
if current_line:
|
||||
lines.append(current_line)
|
||||
|
||||
# 限制显示行数,避免溢出
|
||||
lines = lines[:4]
|
||||
|
||||
# 计算起始Y坐标以垂直居中
|
||||
total_height = len(lines) * 20
|
||||
start_y = 50 + (90 - total_height) // 2
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
# 计算水平居中
|
||||
line_width = 0
|
||||
for c in line:
|
||||
line_width += 16 if ord(c) > 127 else 8
|
||||
|
||||
center_x = 20 + (200 - line_width) // 2
|
||||
self.text(line, center_x, start_y + i * 20, st7789.WHITE, wait=False)
|
||||
else:
|
||||
self.text("未识别到文字", 70, 85, st7789.WHITE)
|
||||
|
||||
# Buttons
|
||||
self.tft.fill_rect(20, 160, 90, 30, st7789.GREEN)
|
||||
self.text("短按确认", 30, 168, st7789.BLACK)
|
||||
|
||||
self.tft.fill_rect(130, 160, 90, 30, st7789.RED)
|
||||
self.text("长按重录", 140, 168, st7789.WHITE)
|
||||
|
||||
9
font.py
9
font.py
@@ -54,9 +54,11 @@ class Font:
|
||||
|
||||
d = binascii.unhexlify(parts[2])
|
||||
self.cache[c] = d
|
||||
# 清除重试计数(如果有)
|
||||
# 清除重试计数(如果有)和 pending
|
||||
if c in self.retry_count:
|
||||
del self.retry_count[c]
|
||||
if c in self.pending_requests:
|
||||
self.pending_requests.remove(c)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Font data parse error: {e}")
|
||||
@@ -110,6 +112,11 @@ class Font:
|
||||
if wait:
|
||||
print(f"Batch requesting fonts: {req_str}")
|
||||
try:
|
||||
# Add Pending requests to retry count to avoid spamming
|
||||
for c in missing_list:
|
||||
if c not in self.pending_requests:
|
||||
self.pending_requests.add(c)
|
||||
|
||||
self.ws.send(f"GET_FONTS_BATCH:{req_str}")
|
||||
if wait:
|
||||
self._wait_for_fonts(missing_codes)
|
||||
|
||||
311
main.py
311
main.py
@@ -28,7 +28,7 @@ UI_SCREEN_RECORDING = 1
|
||||
UI_SCREEN_CONFIRM = 2
|
||||
UI_SCREEN_RESULT = 3
|
||||
|
||||
BOOT_SHORT_MS = 500
|
||||
BOOT_SHORT_MS = 100
|
||||
BOOT_LONG_MS = 2000
|
||||
BOOT_EXTRA_LONG_MS = 5000
|
||||
|
||||
@@ -170,7 +170,7 @@ def draw_progress_bar(display, x, y, width, height, progress, color=st7789.CYAN)
|
||||
display.tft.fill_rect(x, y, bar_width, height, color)
|
||||
|
||||
|
||||
def render_recording_screen(display, asr_text="", audio_level=0):
|
||||
def render_recording_screen(display, asr_text="", audio_level=0, is_recording=False):
|
||||
"""渲染录音界面"""
|
||||
if not display or not display.tft:
|
||||
return
|
||||
@@ -190,7 +190,10 @@ def render_recording_screen(display, asr_text="", audio_level=0):
|
||||
display.text(asr_text[:20], 20, 130, st7789.WHITE, wait=False)
|
||||
|
||||
display.tft.fill_rect(60, 200, 120, 25, st7789.RED)
|
||||
display.text("松开停止", 85, 205, st7789.WHITE)
|
||||
if is_recording:
|
||||
display.text("松开停止", 85, 205, st7789.WHITE)
|
||||
else:
|
||||
display.text("长按录音", 85, 205, st7789.WHITE)
|
||||
|
||||
|
||||
def render_confirm_screen(display, asr_text=""):
|
||||
@@ -251,14 +254,14 @@ def render_result_screen(display, status="", prompt="", image_received=False):
|
||||
display.text("AI 生成中", 80, 8, st7789.BLACK)
|
||||
display.text("生成失败", 80, 50, st7789.RED)
|
||||
|
||||
if prompt and not image_received and not image_generation_done:
|
||||
if prompt and not image_received:
|
||||
display.tft.fill_rect(10, 140, 220, 50, 0x2124) # Dark Grey
|
||||
display.text("提示词:", 15, 145, st7789.CYAN)
|
||||
display.text(prompt[:25] + "..." if len(prompt) > 25 else prompt, 15, 165, st7789.WHITE)
|
||||
|
||||
# Only show back button if not showing full image, or maybe show it transparently?
|
||||
# For now, let's not cover the image with the button hint
|
||||
if not image_received and not image_generation_done:
|
||||
if not image_received:
|
||||
display.tft.fill_rect(60, 210, 120, 25, st7789.BLUE)
|
||||
display.text("长按返回", 90, 215, st7789.WHITE)
|
||||
|
||||
@@ -502,19 +505,20 @@ def main():
|
||||
# WiFi 和 WS 都连接成功后,进入录音界面
|
||||
ui_screen = UI_SCREEN_RECORDING
|
||||
if display.tft:
|
||||
render_recording_screen(display, "", 0)
|
||||
render_recording_screen(display, "", 0, False)
|
||||
else:
|
||||
print("Running in offline mode")
|
||||
# 即使离线也进入录音界面(虽然不能用)
|
||||
ui_screen = UI_SCREEN_RECORDING
|
||||
if display.tft:
|
||||
render_recording_screen(display, "离线模式", 0)
|
||||
render_recording_screen(display, "离线模式", 0, False)
|
||||
|
||||
read_buf = bytearray(4096)
|
||||
last_audio_level = 0
|
||||
memory_check_counter = 0
|
||||
spinner_angle = 0
|
||||
last_spinner_time = 0
|
||||
wait_for_release = False
|
||||
|
||||
while True:
|
||||
try:
|
||||
@@ -544,134 +548,67 @@ def main():
|
||||
|
||||
btn_action = get_boot_button_action(boot_btn)
|
||||
|
||||
if btn_action == 1:
|
||||
if is_recording:
|
||||
print(">>> Stop recording")
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
ws.send("STOP_RECORDING")
|
||||
except:
|
||||
ws = None
|
||||
|
||||
is_recording = False
|
||||
ui_screen = UI_SCREEN_RESULT
|
||||
image_generation_done = False
|
||||
|
||||
if display.tft:
|
||||
render_result_screen(display, "OPTIMIZING", current_asr_text, False)
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
elif ui_screen == UI_SCREEN_RECORDING:
|
||||
if not is_recording:
|
||||
print(">>> Recording...")
|
||||
is_recording = True
|
||||
confirm_waiting = False
|
||||
current_asr_text = ""
|
||||
current_prompt = ""
|
||||
current_status = ""
|
||||
image_generation_done = False
|
||||
|
||||
if display.tft:
|
||||
render_recording_screen(display, "", 0)
|
||||
|
||||
if ws is None or not ws.is_connected():
|
||||
connect_ws()
|
||||
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
ws.send("START_RECORDING")
|
||||
except:
|
||||
ws = None
|
||||
|
||||
elif ui_screen == UI_SCREEN_CONFIRM:
|
||||
print(">>> Confirm and generate")
|
||||
|
||||
# 发送生成图片指令
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
# 明确发送生成指令
|
||||
ws.send(f"GENERATE_IMAGE:{current_asr_text}")
|
||||
except:
|
||||
ws = None
|
||||
|
||||
is_recording = False
|
||||
ui_screen = UI_SCREEN_RESULT
|
||||
image_generation_done = False
|
||||
|
||||
if display.tft:
|
||||
render_result_screen(display, "OPTIMIZING", current_asr_text, False)
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
elif ui_screen == UI_SCREEN_RESULT:
|
||||
# Ignore short press in result screen to keep image displayed
|
||||
# unless image generation failed or is still in progress?
|
||||
# User request: "只有长按boot才离开" (Only leave on long press)
|
||||
# So we do nothing here.
|
||||
pass
|
||||
|
||||
elif btn_action == 2:
|
||||
if is_recording:
|
||||
print(">>> Stop recording (long press)")
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
ws.send("STOP_RECORDING")
|
||||
except:
|
||||
ws = None
|
||||
|
||||
is_recording = False
|
||||
|
||||
# If in recording screen or (not recording AND not result screen), then regenerate/re-record
|
||||
# This ensures result screen is handled by its own block below
|
||||
if ui_screen == UI_SCREEN_RECORDING:
|
||||
if current_asr_text:
|
||||
print(">>> Generate image with ASR text")
|
||||
ui_screen = UI_SCREEN_RESULT
|
||||
image_generation_done = False
|
||||
|
||||
if display.tft:
|
||||
render_result_screen(display, "OPTIMIZING", current_asr_text, False)
|
||||
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
print(">>> Re-record")
|
||||
current_asr_text = ""
|
||||
confirm_waiting = False
|
||||
ui_screen = UI_SCREEN_RECORDING
|
||||
|
||||
if display.tft:
|
||||
render_recording_screen(display, "", 0)
|
||||
|
||||
elif ui_screen == UI_SCREEN_CONFIRM:
|
||||
print(">>> Re-record")
|
||||
current_asr_text = ""
|
||||
# Hold to Record Logic (Press to Start, Release to Stop)
|
||||
if ui_screen == UI_SCREEN_RECORDING:
|
||||
if boot_btn.value() == 0 and not is_recording:
|
||||
print(">>> Start recording (Hold)")
|
||||
is_recording = True
|
||||
confirm_waiting = False
|
||||
ui_screen = UI_SCREEN_RECORDING
|
||||
|
||||
if display.tft:
|
||||
render_recording_screen(display, "", 0)
|
||||
|
||||
elif ui_screen == UI_SCREEN_RESULT:
|
||||
print(">>> Back to recording")
|
||||
# Stop recording if it was somehow started or just reset state
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
ws.send("STOP_RECORDING")
|
||||
except:
|
||||
ws = None
|
||||
|
||||
ui_screen = UI_SCREEN_RECORDING
|
||||
is_recording = False
|
||||
current_asr_text = ""
|
||||
current_prompt = ""
|
||||
current_status = ""
|
||||
image_generation_done = False
|
||||
confirm_waiting = False
|
||||
|
||||
if display.tft:
|
||||
render_recording_screen(display, "", 0)
|
||||
render_recording_screen(display, "", 0, True)
|
||||
if ws is None or not ws.is_connected():
|
||||
connect_ws()
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
ws.send("START_RECORDING")
|
||||
except:
|
||||
ws = None
|
||||
elif boot_btn.value() == 1 and is_recording:
|
||||
print(">>> Stop recording (Release)")
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
ws.send("STOP_RECORDING")
|
||||
except:
|
||||
ws = None
|
||||
is_recording = False
|
||||
ui_screen = UI_SCREEN_CONFIRM
|
||||
image_generation_done = False
|
||||
if display.tft:
|
||||
render_confirm_screen(display, current_asr_text)
|
||||
# Consume action to prevent triggering other events
|
||||
btn_action = 0
|
||||
|
||||
if btn_action == 1:
|
||||
if ui_screen == UI_SCREEN_CONFIRM:
|
||||
print(">>> Confirm and generate")
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
ws.send(f"GENERATE_IMAGE:{current_asr_text}")
|
||||
except:
|
||||
ws = None
|
||||
is_recording = False
|
||||
ui_screen = UI_SCREEN_RESULT
|
||||
image_generation_done = False
|
||||
if display.tft:
|
||||
render_result_screen(display, "OPTIMIZING", current_asr_text, False)
|
||||
time.sleep(0.5)
|
||||
|
||||
elif btn_action == 2:
|
||||
if ui_screen == UI_SCREEN_CONFIRM or ui_screen == UI_SCREEN_RESULT:
|
||||
print(">>> Re-record")
|
||||
current_asr_text = ""
|
||||
confirm_waiting = False
|
||||
ui_screen = UI_SCREEN_RECORDING
|
||||
is_recording = False
|
||||
image_generation_done = False
|
||||
if display.tft:
|
||||
render_recording_screen(display, "", 0, False)
|
||||
time.sleep(0.5)
|
||||
|
||||
elif btn_action == 3:
|
||||
print(">>> Config mode")
|
||||
|
||||
@@ -684,82 +621,58 @@ def main():
|
||||
ws.send(read_buf[:num_read], opcode=2)
|
||||
|
||||
# 移除录音时的消息接收,确保录音流畅
|
||||
# poller = uselect.poll()
|
||||
# poller.register(ws.sock, uselect.POLLIN)
|
||||
# events = poller.poll(0)
|
||||
# if events:
|
||||
# msg = ws.recv()
|
||||
# image_state, event_data = process_message(msg, display, image_state, image_data_list)
|
||||
#
|
||||
# if event_data:
|
||||
# if event_data[0] == "asr":
|
||||
# current_asr_text = event_data[1]
|
||||
# if display.tft:
|
||||
# render_recording_screen(display, current_asr_text, last_audio_level)
|
||||
#
|
||||
# elif event_data[0] == "font_update":
|
||||
# if ui_screen == UI_SCREEN_RECORDING and display.tft:
|
||||
# render_recording_screen(display, current_asr_text, last_audio_level)
|
||||
#
|
||||
# elif event_data[0] == "status":
|
||||
# current_status = event_data[1]
|
||||
# status_text = event_data[2] if len(event_data) > 2 else ""
|
||||
# if display.tft:
|
||||
# render_result_screen(display, current_status, current_prompt, image_generation_done)
|
||||
#
|
||||
# elif event_data[0] == "prompt":
|
||||
# current_prompt = event_data[1]
|
||||
#
|
||||
# elif event_data[0] == "image_done":
|
||||
# image_generation_done = True
|
||||
# if display.tft:
|
||||
# render_result_screen(display, "COMPLETE", current_prompt, True)
|
||||
#
|
||||
# elif event_data[0] == "error":
|
||||
# if display.tft:
|
||||
# render_result_screen(display, "ERROR", current_prompt, False)
|
||||
|
||||
except:
|
||||
ws = None
|
||||
|
||||
if ui_screen == UI_SCREEN_RESULT and ws and ws.is_connected():
|
||||
try:
|
||||
poller = uselect.poll()
|
||||
poller.register(ws.sock, uselect.POLLIN)
|
||||
events = poller.poll(100)
|
||||
if events:
|
||||
msg = ws.recv()
|
||||
if msg:
|
||||
image_state, event_data = process_message(msg, display, image_state, image_data_list)
|
||||
|
||||
if event_data:
|
||||
if event_data[0] == "asr":
|
||||
current_asr_text = event_data[1]
|
||||
# 在录音结束后(CONFIRM状态)或 RESULT 状态,才接收消息
|
||||
if (ui_screen == UI_SCREEN_CONFIRM or ui_screen == UI_SCREEN_RESULT or ui_screen == UI_SCREEN_RECORDING) and not is_recording:
|
||||
if ws and ws.is_connected():
|
||||
try:
|
||||
poller = uselect.poll()
|
||||
poller.register(ws.sock, uselect.POLLIN)
|
||||
events = poller.poll(100)
|
||||
if events:
|
||||
msg = ws.recv()
|
||||
if msg:
|
||||
image_state, event_data = process_message(msg, display, image_state, image_data_list)
|
||||
|
||||
elif event_data[0] == "status":
|
||||
current_status = event_data[1]
|
||||
status_text = event_data[2] if len(event_data) > 2 else ""
|
||||
if display.tft:
|
||||
render_result_screen(display, current_status, current_prompt, image_generation_done)
|
||||
|
||||
elif event_data[0] == "prompt":
|
||||
current_prompt = event_data[1]
|
||||
if display.tft:
|
||||
render_result_screen(display, current_status, current_prompt, image_generation_done)
|
||||
|
||||
elif event_data[0] == "image_done":
|
||||
image_generation_done = True
|
||||
if display.tft:
|
||||
render_result_screen(display, "COMPLETE", current_prompt, True)
|
||||
|
||||
elif event_data[0] == "error":
|
||||
if display.tft:
|
||||
render_result_screen(display, "ERROR", current_prompt, False)
|
||||
except:
|
||||
pass
|
||||
if event_data:
|
||||
if event_data[0] == "asr":
|
||||
current_asr_text = event_data[1]
|
||||
print(f"Received ASR: {current_asr_text}")
|
||||
|
||||
# 收到 ASR 结果,跳转到 CONFIRM 界面
|
||||
if ui_screen == UI_SCREEN_RECORDING or ui_screen == UI_SCREEN_CONFIRM:
|
||||
ui_screen = UI_SCREEN_CONFIRM
|
||||
if display.tft:
|
||||
render_confirm_screen(display, current_asr_text)
|
||||
|
||||
elif event_data[0] == "font_update":
|
||||
# 如果还在录音界面等待,刷新一下(虽然可能已经跳到 CONFIRM 了)
|
||||
pass
|
||||
|
||||
elif event_data[0] == "status":
|
||||
current_status = event_data[1]
|
||||
status_text = event_data[2] if len(event_data) > 2 else ""
|
||||
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
||||
render_result_screen(display, current_status, current_prompt, image_generation_done)
|
||||
|
||||
elif event_data[0] == "prompt":
|
||||
current_prompt = event_data[1]
|
||||
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
||||
render_result_screen(display, current_status, current_prompt, image_generation_done)
|
||||
|
||||
elif event_data[0] == "image_done":
|
||||
image_generation_done = True
|
||||
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
||||
render_result_screen(display, "COMPLETE", current_prompt, True)
|
||||
|
||||
elif event_data[0] == "error":
|
||||
if display.tft and ui_screen == UI_SCREEN_RESULT:
|
||||
render_result_screen(display, "ERROR", current_prompt, False)
|
||||
except Exception as e:
|
||||
print(f"WS Recv Error: {e}")
|
||||
|
||||
continue
|
||||
|
||||
time.sleep(0.01)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -9,22 +9,31 @@ FONTS = {
|
||||
20572: b'\x08\x80\x08\x78\x17\x80\x10\x60\x23\xa0\x22\x60\x63\x80\xa0\x7c\x2f\x88\x28\x30\x23\xc0\x20\x40\x21\x40\x20\xc0\x20\x40\x00\x00', # 停
|
||||
21035: b'\x00\x08\x06\x08\x3a\x08\x22\x48\x26\x48\x38\x48\x28\x48\x0f\x48\x71\x48\x11\x48\x11\x08\x22\x08\x2a\x28\x44\x18\x80\x08\x00\x00', # 别
|
||||
21040: b'\x00\x08\x00\x08\x07\x88\x38\x28\x0a\x28\x11\x28\x23\xa8\x7c\xa8\x04\x28\x07\x28\x3c\x28\x07\x88\x18\x28\x60\x18\x00\x08\x00\x00', # 到
|
||||
21151: b'\x00\x40\x00\x40\x00\x40\x06\x40\x78\x78\x09\xc8\x08\x48\x08\x48\x08\x88\x0e\x88\x71\x08\x01\x10\x02\x50\x04\x30\x08\x00\x00\x00', # 功
|
||||
21153: b'\x02\x00\x02\x00\x04\xe0\x07\x40\x0a\x80\x11\x00\x22\xc0\x0d\x30\x31\x0e\xc1\xe0\x1e\x20\x02\x20\x04\x40\x19\x40\x60\x80\x00\x00', # 务
|
||||
21160: b'\x00\x20\x00\x20\x06\x20\x18\x20\x00\x38\x0e\xe8\x70\x28\x10\x48\x14\x48\x22\x88\x2e\x88\x31\x10\x01\x50\x02\x20\x04\x00\x00\x00', # 动
|
||||
21270: b'\x04\x00\x04\x80\x08\x80\x08\x88\x08\x88\x18\x90\x28\xa0\x48\xc0\x09\x80\x0a\x80\x08\x84\x08\x84\x08\x84\x08\x7c\x08\x00\x00\x00', # 化
|
||||
21527: b'\x00\x60\x03\xa0\x00\x20\x19\x20\x69\x20\x49\x20\x59\x20\x61\x78\x01\x88\x00\x08\x00\xe8\x0f\x08\x00\x10\x00\x50\x00\x20\x00\x00', # 吗
|
||||
21551: b'\x01\x00\x00\x80\x00\x70\x0f\x90\x08\x10\x08\x70\x0f\x80\x08\x00\x10\x70\x17\x90\x14\x10\x24\x10\x24\x70\x47\x80\x84\x00\x00\x00', # 启
|
||||
22120: b'\x00\x00\x06\x30\x1a\xd0\x12\x90\x16\xb0\x18\xc0\x12\x20\x03\xf8\x7c\x40\x08\x20\x36\x3e\xda\xd0\x12\x90\x16\xb0\x18\xc0\x00\x00', # 器
|
||||
22238: b'\x00\x00\x00\x00\x01\xf8\x3e\x08\x20\x08\x21\x88\x26\x88\x24\x88\x25\x88\x26\x08\x20\x08\x20\xf8\x3f\x00\x00\x00\x00\x00\x00\x00', # 回
|
||||
22312: b'\x01\x00\x01\x00\x02\x00\x03\xf8\x7c\x00\x04\x80\x18\x80\x10\x80\x30\xf0\x57\x80\x90\x80\x10\x80\x10\xfc\x1f\x00\x10\x00\x00\x00', # 在
|
||||
22833: b'\x01\x00\x09\x00\x09\x00\x09\xf0\x1f\x00\x11\x00\x21\x00\x01\xf8\x7e\x80\x02\x80\x04\x40\x04\x40\x08\x20\x10\x38\x20\x00\x00\x00', # 失
|
||||
23383: b'\x02\x00\x01\x00\x01\xfc\x3e\x08\x21\xe0\x0e\x40\x00\x80\x01\x00\x00\xfc\x7f\x80\x00\x80\x00\x80\x00\x80\x02\x80\x01\x00\x00\x00', # 字
|
||||
23436: b'\x02\x00\x01\x00\x00\xfc\x3f\x08\x20\x00\x00\xc0\x07\x00\x00\x78\x3f\x80\x04\x80\x04\x80\x08\x84\x08\x84\x10\x84\x60\x7c\x00\x00', # 完
|
||||
24320: b'\x00\x00\x03\xf0\x1c\x40\x04\x40\x04\x40\x04\x40\x07\xfc\x7c\x40\x04\x40\x04\x40\x08\x40\x08\x40\x10\x40\x20\x40\x40\x40\x00\x00', # 开
|
||||
24335: b'\x01\x00\x01\x40\x01\x20\x01\x00\x01\x78\x3f\x80\x00\x80\x03\x80\x1c\x80\x04\x40\x04\x40\x07\x24\x18\x14\x60\x0c\x00\x04\x00\x00', # 式
|
||||
24405: b'\x01\xc0\x0e\x40\x01\xc0\x0e\x40\x00\xfc\x3f\x00\x01\x10\x11\x10\x09\xa0\x05\x40\x09\x20\x11\x18\x61\x06\x03\x00\x01\x00\x00\x00', # 录
|
||||
24605: b'\x00\xf0\x1f\x10\x11\x10\x11\xf0\x1f\x10\x11\x10\x11\xf0\x1e\x00\x10\x00\x23\x18\x28\x84\x24\x10\x43\x10\x40\xf0\x00\x00\x00\x00', # 思
|
||||
25104: b'\x00\xa0\x00\x90\x00\x80\x00\xf0\x1f\x80\x10\x90\x10\x90\x1e\xa0\x12\xa0\x22\x40\x22\x44\x24\xa4\x55\x14\x48\x0c\x80\x04\x00\x00', # 成
|
||||
25353: b'\x10\x40\x10\x20\x10\x3c\x13\xc8\x1e\x40\x70\x40\x10\x80\x18\xfe\x37\x10\xd1\x10\x11\x20\x10\xa0\x50\x60\x31\x90\x16\x08\x00\x00', # 按
|
||||
25509: b'\x10\x80\x10\x40\x10\x18\x13\xf0\x1d\x10\x70\xa0\x14\xfc\x1b\x80\x30\x80\xd7\xfc\x11\x20\x12\x40\x11\xc0\x30\xa0\x17\x18\x00\x00', # 接
|
||||
25552: b'\x10\x30\x11\xd0\x11\x10\x11\xd0\x1d\x30\x71\xc0\x15\x00\x18\x38\x37\xc0\xd2\x70\x12\x40\x13\x40\x54\xc0\x34\x30\x18\x0e\x00\x00', # 提
|
||||
25991: b'\x02\x00\x01\x00\x01\x00\x00\x38\x3f\xc0\x00\x40\x04\x40\x02\x80\x02\x80\x01\x00\x01\x00\x02\x80\x0c\x40\x30\x30\xc0\x0e\x00\x00', # 文
|
||||
26381: b'\x00\x30\x0d\xd0\x35\x10\x25\x50\x2d\x20\x35\x00\x25\x30\x2d\xd0\x35\x90\x25\x50\x25\x20\x45\x30\x45\x48\x4d\x86\x85\x00\x00\x00', # 服
|
||||
26410: b'\x01\x00\x01\x00\x01\x00\x01\xf0\x1f\x00\x01\x00\x01\x78\x7f\x80\x03\x40\x05\x40\x09\x20\x11\x20\x61\x1c\x81\x00\x01\x00\x00\x00', # 未
|
||||
26494: b'\x00\x40\x10\x40\x10\xa0\x10\xa0\x1c\xa0\x71\x10\x19\x50\x36\x4c\x52\x40\x54\x80\x90\xa0\x11\x10\x11\x38\x13\xc8\x10\x00\x00\x00', # 松
|
||||
27169: b'\x11\x10\x11\x10\x11\xfc\x13\x20\x1d\xf0\x73\x10\x1a\x70\x37\x90\x52\x70\x53\xc0\x90\x7c\x17\xa0\x11\x10\x12\x08\x14\x0e\x00\x00', # 模
|
||||
27490: b'\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x11\x00\x11\x30\x11\xc0\x11\x00\x11\x00\x11\x00\x11\x00\x11\x00\x11\xfc\xfe\x00\x00\x00', # 止
|
||||
27491: b'\x00\x00\x00\xf0\x1f\x00\x01\x00\x01\x00\x01\x00\x09\x30\x09\xc0\x09\x00\x09\x00\x09\x00\x09\x00\x09\xfc\x7e\x00\x00\x00\x00\x00', # 正
|
||||
29983: b'\x01\x00\x01\x00\x01\x00\x09\x00\x09\x00\x11\xf0\x1f\x00\x21\x00\x21\x00\x41\xe0\x0f\x00\x01\x00\x01\x00\x01\xfc\x7e\x00\x00\x00', # 生
|
||||
@@ -32,16 +41,25 @@ FONTS = {
|
||||
30701: b'\x10\x00\x10\x3c\x11\xc0\x16\x18\x38\xe8\x28\x88\x48\x98\x0e\xe0\x78\x10\x08\x90\x14\x50\x12\x50\x20\x3c\x43\xc0\x00\x00\x00\x00', # 短
|
||||
30830: b'\x00\x80\x00\x80\x0c\xf0\x71\x20\x11\x40\x12\x78\x21\xc8\x2d\x68\x75\xc8\xa5\x68\x2d\xc8\x32\x48\x22\x48\x04\x18\x08\x08\x00\x00', # 确
|
||||
31034: b'\x00\x00\x00\xe0\x0f\x00\x00\x00\x00\xfc\x7f\x00\x01\x00\x05\x00\x05\x20\x09\x10\x11\x08\x21\x08\x45\x00\x03\x00\x01\x00\x00\x00', # 示
|
||||
31163: b'\x02\x00\x01\xf8\x3f\x00\x02\xa0\x09\x20\x0a\xe0\x0f\x00\x09\x00\x01\xf0\x3e\x90\x22\x50\x25\xd0\x26\x10\x20\x30\x20\x10\x00\x00', # 离
|
||||
32440: b'\x10\x00\x10\x10\x10\x60\x21\xa0\x29\x20\x49\x20\xf1\x3c\x11\xe0\x11\x20\x2d\x20\x71\x10\x01\x52\x0d\x8a\x31\x06\xc0\x02\x00\x00', # 纸
|
||||
32447: b'\x01\x00\x11\x40\x11\x20\x11\x00\x25\x60\x27\x80\x78\x98\x08\xe0\x13\x90\x38\x90\x00\xa0\x04\x44\x19\xa4\x66\x1c\x00\x04\x00\x00', # 线
|
||||
32472: b'\x00\x80\x10\x80\x10\xc0\x21\x40\x25\x20\x4a\x10\x74\x6e\x11\x80\x2c\x38\x73\xc0\x00\x80\x0c\xa0\x31\x10\xc2\x78\x03\x88\x00\x00', # 绘
|
||||
32771: b'\x02\x00\x02\x10\x03\xa0\x0e\x40\x02\x80\x03\xfc\x7e\x00\x07\xf0\x1a\x00\x22\x60\xc3\xa0\x00\x20\x00\x40\x01\x40\x00\x80\x00\x00', # 考
|
||||
35748: b'\x00\x00\x10\x40\x08\x40\x08\x40\x00\x40\x00\x40\x70\x40\x10\x40\x10\xa0\x10\xa0\x15\x10\x19\x10\x12\x08\x04\x0e\x08\x00\x00\x00', # 认
|
||||
35782: b'\x00\x00\x10\x38\x09\xc8\x09\x08\x01\x08\x71\x38\x11\xc0\x11\x00\x10\x00\x14\x90\x18\x88\x11\x04\x02\x04\x04\x00\x00\x00\x00\x00', # 识
|
||||
35789: b'\x20\x00\x10\x78\x0b\x88\x00\x08\x00\xe8\x77\x08\x10\xc8\x13\x48\x12\x48\x12\xc8\x13\x08\x1a\x08\x10\x28\x00\x18\x00\x08\x00\x00', # 词
|
||||
35797: b'\x00\x40\x20\x50\x10\x48\x10\x40\x00\x78\x07\xc0\x70\x40\x10\xc0\x17\x40\x11\x20\x11\x20\x15\x54\x19\x94\x16\x0c\x00\x04\x00\x00', # 试
|
||||
35821: b'\x00\x00\x20\x70\x13\x80\x10\x80\x00\xe0\x03\x20\xe1\x20\x21\xfc\x26\x00\x20\x70\x23\x90\x2a\x10\x32\x70\x23\x80\x02\x00\x00\x00', # 语
|
||||
35828: b'\x02\x10\x21\x10\x11\x20\x10\x20\x00\x70\x03\x90\x72\x10\x12\x70\x13\xa0\x10\xa0\x14\xa0\x19\x22\x11\x22\x02\x22\x0c\x1e\x00\x00', # 说
|
||||
35831: b'\x00\x40\x20\x70\x13\xc0\x10\x70\x01\xc0\x00\x7c\x77\x80\x10\x70\x13\x90\x12\x70\x13\x90\x1a\x70\x13\x90\x02\x30\x02\x10\x00\x00', # 请
|
||||
36133: b'\x00\x40\x06\x40\x3a\x40\x22\x4c\x2a\x70\x2a\x90\x2a\x90\x2b\x50\x2a\x50\x28\x20\x14\x20\x12\x50\x20\x90\x21\x0c\x42\x00\x00\x00', # 败
|
||||
36148: b'\x00\x20\x06\x20\x3a\x20\x22\x20\x2a\x3c\x2a\x20\x2a\x20\x2a\x20\x2a\x38\x2a\xc8\x28\x88\x14\x88\x22\x98\x42\xe0\x80\x80\x00\x00', # 贴
|
||||
36820: b'\x00\x00\x00\x38\x13\xc0\x0a\x00\x02\x70\x03\x90\x3a\x10\xca\xa0\x12\x60\x12\x50\x0c\x88\x09\x00\x7c\x00\x01\xc0\x00\x3e\x00\x00', # 返
|
||||
36830: b'\x00\x40\x20\x40\x10\x78\x13\x80\x01\x40\x1a\x70\x6b\xc0\x08\x40\x10\x78\x17\xc0\x08\x40\x70\x40\x1e\x00\x01\xc0\x00\x3e\x00\x00', # 连
|
||||
36857: b'\x00\x80\x00\x40\x20\x1c\x13\xe0\x00\xa0\x02\xa0\x1a\xb0\x6a\xa8\x15\x24\x11\x20\x0a\x60\x10\x20\x7c\x00\x03\xc0\x00\x3c\x00\x00', # 迹
|
||||
37325: b'\x00\x20\x00\xc0\x1f\x00\x01\xfc\x7f\x00\x01\xf0\x1f\x10\x11\xd0\x17\x10\x11\xf0\x1f\x00\x01\xe0\x1f\x00\x01\xfc\x7e\x00\x00\x00', # 重
|
||||
37327: b'\x01\xe0\x0e\x20\x09\xe0\x0e\x20\x08\xfc\x7f\x00\x00\xe0\x1f\x20\x11\xe0\x17\x20\x11\xe0\x1f\x00\x01\x00\x01\xf8\x3e\x00\x00\x00', # 量
|
||||
38271: b'\x08\x00\x08\x20\x08\x40\x08\x80\x0b\x00\x0c\x00\x09\xf8\x7e\x00\x0a\x00\x09\x00\x08\x80\x08\x40\x0a\x30\x0c\x0c\x08\x00\x00\x00', # 长
|
||||
38899: b'\x02\x00\x01\x00\x01\xf0\x1e\x40\x04\x40\x04\x80\x01\xfc\x7e\x00\x01\xe0\x0e\x20\x09\xa0\x0e\x20\x08\x20\x09\xe0\x0e\x20\x00\x00', # 音
|
||||
65311: b'\x00\x00\x00\x00\x1c\x00\x22\x00\x22\x00\x04\x00\x08\x00\x08\x00\x08\x00\x00\x00\x18\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00', # ?
|
||||
|
||||
Binary file not shown.
@@ -21,7 +21,18 @@ FIXED_STRINGS = [
|
||||
"生成完成!",
|
||||
"生成失败",
|
||||
"提示词:",
|
||||
"返回录音"
|
||||
"返回录音",
|
||||
"重试",
|
||||
"长按返回",
|
||||
"连接服务器...",
|
||||
"服务器连接失败",
|
||||
"离线模式",
|
||||
"量迹AI贴纸生成",
|
||||
"正在启动...",
|
||||
"WiFi连接中...",
|
||||
"WiFi连接成功!",
|
||||
"WiFi连接失败",
|
||||
"请重试"
|
||||
]
|
||||
|
||||
def generate_static_font():
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -851,6 +851,17 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||
# 4. 如果有识别结果,发送ASR文字到ESP32
|
||||
if asr_text:
|
||||
print(f"ASR result: {asr_text}")
|
||||
|
||||
# 主动下发字体数据
|
||||
try:
|
||||
unique_chars = set(asr_text)
|
||||
code_list = [str(ord(c)) for c in unique_chars]
|
||||
print(f"Sending font data for {len(code_list)} characters...")
|
||||
success_count, failed = await send_font_batch_with_retry(websocket, code_list)
|
||||
print(f"Font data sent: {success_count} success, {len(failed)} failed")
|
||||
except Exception as e:
|
||||
print(f"Error sending font data: {e}")
|
||||
|
||||
# 发送 ASR 文字到 ESP32 显示
|
||||
await websocket.send_text(f"ASR:{asr_text}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user