From 7e339324a2a2158a0cabeee30b4bec41f9233586 Mon Sep 17 00:00:00 2001 From: kicer Date: Fri, 30 Jan 2026 23:42:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E8=B0=83=E8=AF=95=E9=80=9A?= =?UTF-8?q?=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rom/app.py | 127 +++++++++++++++++++++++---------------------- src/rom/display.py | 52 +++++++++++-------- utils/download.sh | 2 +- 3 files changed, 97 insertions(+), 84 deletions(-) diff --git a/src/rom/app.py b/src/rom/app.py index e06feab..0787711 100644 --- a/src/rom/app.py +++ b/src/rom/app.py @@ -7,7 +7,7 @@ import sys import time import machine -import uasyncio +import asyncio from config import config from display import display # 导入液晶屏管理模块 from wifi_manager import wifi_manager @@ -15,6 +15,8 @@ from wifi_manager import wifi_manager # 全局变量存储最新的天气数据 latest_weather = None +def uuid(): + return str(machine.unique_id().hex()) def parse_url_params(url): # 解析URL中的查询参数,返回参数字典 @@ -42,11 +44,13 @@ def sync_ntp_time(): rtc = machine.RTC() tm = time.gmtime(t) rtc.datetime((tm[0], tm[1], tm[2], tm[6], tm[3], tm[4], tm[5], 0)) + + # 同步完成后,等待调度自动更新UI print(f"时间同步:{rtc.datetime()}") # 简化的天气数据获取函数 -def get_weather_data(city=None, force=False): +async def get_weather_data(city=None, force=False): """获取天气数据,返回JSON格式数据 Args: @@ -62,50 +66,50 @@ def get_weather_data(city=None, force=False): return latest_weather try: - import urequests + import aiohttp # 从配置文件获取城市,如果没有提供则使用配置中的值 if city is None: city = config.get("city", "北京") - # 从配置获取API基础URL,默认使用官方API - api_base = config.get("weather_api_url", "https://iot.foresh.com/api/weather") - url = f"{api_base}?city={city}" print(f"正在获取{city}天气数据...") + # 从配置获取API基础URL,默认使用官方API + url = config.get("weather_api_url", "http://esp.tangofu.com/api/sd2/") + params={'uuid':uuid(), 'city':city} # 发送GET请求 - response = urequests.get(url) + print('start http') + async with aiohttp.ClientSession() as session: + async with session.get(url, params=params) as response: + print('ACK=%s'%response.status) + # 检查响应状态 + if response.status == 200: + # 解析JSON数据 + wdata = await response.json() - # 检查响应状态 - if response.status_code == 200: - # 解析JSON数据 - wdata = response.json() - response.close() # 关闭连接释放内存 + # 提取关键信息,减少内存占用 + city = wdata.get("city", "N/A") + weather = wdata.get("weather", 0) + t = wdata.get("temperature", "-") + rh = wdata.get("humidity", "-") + pm = wdata.get("pm25", "-") + ap = wdata.get("atmosp", None) + co2 = wdata.get("co2", None) + aqi = wdata.get("aqi", 0) + advice = wdata.get("advice", []) + lunar = wdata.get("lunar", None) - # 提取关键信息,减少内存占用 - weather_report = { - "t": wdata.get("temperature", "N/A"), - "rh": wdata.get("humidity", "N/A"), - "pm25": wdata.get("pm25", "N/A"), - "weather": wdata.get("weather", "N/A"), - "t_min": wdata.get("t_min", "N/A"), - "t_max": wdata.get("t_max", "N/A"), - "city": wdata.get("city", "N/A"), - "aqi": wdata.get("aqi", "N/A"), - "date": wdata.get("date", "N/A"), - "advice": wdata.get("advice", "N/A"), - "image": wdata.get("image", "N/A"), - } + display.update_ui(city, weather, advice, aqi, lunar, + envdat={'t':t,'rh':rh,'co2':co2,'pm':pm,'ap':ap}) - print("天气数据获取成功") - # 更新缓存 - latest_weather = weather_report + # 更新缓存 + latest_weather = display.ui_data + print("天气数据获取成功") - return weather_report - else: - print(f"获取天气数据失败,状态码: {response.status_code}") - response.close() - return None + return latest_weather + else: + print(f"获取天气数据失败,状态码: {response.status}") + return None except Exception as e: print(f"获取天气数据出错: {e}") @@ -134,9 +138,10 @@ async def sysinfo_update_task(): # 更新天气数据 gc.collect() task_id = "weather" - get_weather_data(force=True) + await get_weather_data(force=True) last_weather = current_ticks - weather_ts = int(config.get("weather_ts", 600)) # 10min + #todo:weather_ts = int(config.get("weather_ts", 600)) # 10min + weather_ts = int(config.get("weather_ts", 6)) # 10min elif ntp_diff >= ntptime_ts * 1000: # 更新NTP时间 gc.collect() @@ -153,11 +158,11 @@ async def sysinfo_update_task(): # 等待x秒再检查(1~30) _x = min(30, 1 + min(weather_ts, ntptime_ts) // 10) - await uasyncio.sleep(_x) + await asyncio.sleep(_x) -# 精简的动画显示任务 -async def animation_task(): +# LCD UI显示任务 +async def ui_task(): """显示JPG动画的后台任务""" # 检查液晶屏是否已初始化 if not display.is_ready(): @@ -165,35 +170,37 @@ async def animation_task(): return try: - # 动画参数 - frame_count = 20 - frame_delay = 100 # 帧延迟(毫秒) + # 延迟2s后开始更新 + await asyncio.sleep_ms(2 * 1000) - await uasyncio.sleep_ms(2 * 1000) - print(f"开始JPG动画,帧延迟: {frame_delay}ms") - - frame = 0 + F = 0 while True: try: - # 计算当前帧号(1-20) - current_frame = (frame % frame_count) + 1 - filename = f"/rom/images/T{current_frame}.jpg" + t0 = time.ticks_ms() - # 显示当前帧,右下角 - display.show_jpg(filename, 160, 160) + # 计算当前帧号(1-10),更新动画 + cframe = (F % 10) + pic = f"/rom/images/T{cframe}.jpg" + display.show_jpg(pic, 160, 160) - # 控制帧率 - await uasyncio.sleep_ms(frame_delay) + # 每隔100帧,更新一次UI显示 + F += 1 + if F % 100 == 0: + display.update_ui() # 每轮清理一次内存 gc.collect() - frame += 2 + # 控制帧率 + now = time.ticks_ms() + ts = time.ticks_diff(now, t0) + _sT = (100-ts) if ts<90 else 10 + await asyncio.sleep_ms(_sT) except Exception as e: print(f"动画帧错误: {e}") # 出错后等待1秒再继续 - await uasyncio.sleep_ms(1000) + await asyncio.sleep_ms(1000) except Exception as e: print(f"动画任务初始化失败: {e}") @@ -214,8 +221,6 @@ def start(): #display.init_display(config.get("bl_mode") != "gpio") display.init_display(False, buffer_size=5000) display.brightness(int(config.get("brightness", 10))) - display.show_jpg("/rom/images/T1.jpg", 80, 80) - gc.collect() cb_progress("WiFi connect ...") if not wifi_manager.connect(cb_progress): @@ -257,7 +262,7 @@ def start(): ), "uptime": str(f"{time.ticks_ms() // 1000} sec"), "memory": str(f"{gc.mem_free() // 1000} KB"), - "uuid": str(machine.unique_id().hex()), + "uuid": uuid, "platform": str(sys.platform), "version": str(sys.version), } @@ -330,7 +335,7 @@ def start(): cmd = json.loads(post_data).get("cmd") token = json.loads(post_data).get("token") - if cmd and token == machine.unique_id().hex(): + if cmd and token == uuid: _NS = {} exec(cmd, _NS) ack["result"] = str(_NS.get("R")) @@ -375,14 +380,14 @@ def start(): await request.write(json.dumps(ack)) # create task - loop = uasyncio.get_event_loop() + loop = asyncio.get_event_loop() loop.create_task(naw.run()) # 启动定时更新任务 loop.create_task(sysinfo_update_task()) # 启动动画显示任务 - loop.create_task(animation_task()) + loop.create_task(ui_task()) gc.collect() print(f"success: {gc.mem_free()}...") diff --git a/src/rom/display.py b/src/rom/display.py index fc17da4..7b9f47b 100644 --- a/src/rom/display.py +++ b/src/rom/display.py @@ -5,10 +5,9 @@ 适用于ESP8266等内存有限的平台。 """ -import gc - -import st7789 - +def _print_mem(): + import gc + gc.collect(); print(f'LCD.mem: {gc.mem_free()}') class Display: """液晶屏管理类 - 单例模式""" @@ -34,6 +33,7 @@ class Display: self.vector_font = '/rom/fonts/en.hfont' self.cn_font = '/rom/fonts/cn.bfont' self.time_font = '/rom/fonts/time.bfont' + self.bootimg = '/rom/images/T0.jpg' # 前景色、背景色、提示框背景色 self._COLORS = (0xFE19, 0x0000, 0x7800) self.ui_type = 'default' @@ -45,9 +45,10 @@ class Display: """初始化液晶屏,默认2048够用且不易有内存碎片""" try: from machine import PWM, SPI, Pin + from st7789 import ST7789 # 初始化显示屏 - self.tft = st7789.ST7789( + self.tft = ST7789( SPI(1, 40_000_000, polarity=1), 240, 240, @@ -65,13 +66,14 @@ class Display: # 初始化并清屏 self.tft.init() - gc.collect() - self.tft.fill(st7789.BLACK) - print("液晶屏初始化成功") + self.tft.fill(0) + display.show_jpg(self.bootimg, 80, 80) + + _print_mem() return True except Exception as e: - print(f"液晶屏初始化失败: {e}") + print(f"LCD init failed: {e}") self.tft = None self._backlight = None return False @@ -80,13 +82,13 @@ class Display: """检查液晶屏是否已初始化""" return self.tft is not None - def clear(self, color=st7789.BLACK): + def clear(self, color=0): """清屏""" self.tft.fill(color) self._pos_y0 = 10 - def show_jpg(self, filename, x=0, y=0, mode=st7789.SLOW): - """显示JPG图片""" + def show_jpg(self, filename, x=0, y=0, mode=1): + """显示JPG图片, 默认SLOW模式""" self.tft.jpg(filename, x, y, mode) def brightness(self, _brightness=-1): @@ -105,7 +107,7 @@ class Display: self._brightness = _brightness return self._brightness - def message(self, msg, x=10, y=None, fg=st7789.WHITE, bg=st7789.BLACK): + def message(self, msg, x=10, y=None, fg=0xFFFF, bg=0): if y == None: y = self._pos_y0 self.tft.text(self.en_font, msg, x, y, fg, bg) @@ -152,9 +154,9 @@ class Display: if self.tft.write(self.cn_font, city, 15,10) == 0: # 城市可能未包括,则显示?? self.tft.write(self.cn_font, '??', 15,10) - # 天气符号: 0-7 (晴、云、雨、雷、雪、雾、风、未知) + # 天气符号: 0-7 (未知、晴、云、雨、雷、雪、雾、风) if weather is not None and weather != self.ui_data.get('weather'): - self.tft.jpg(f"/rom/images/{weather}.jpg",165,10,st7789.SLOW) + self.show_jpg(f"/rom/images/{weather}.jpg",165,10) self.ui_data['weather'] = weather # 建议信息可能有很多条,需要轮换展示 if advice is not None and advice != self.ui_data.get('advice'): @@ -191,7 +193,7 @@ class Display: # 如果co2数据存在,优先显示co2 if self.ui_data.get('ap'): self.ui_data['ap'] = None - self.tft.jpg("/rom/images/co2.jpg",85,209,st7789.SLOW) + self.show_jpg("/rom/images/co2.jpg",85,209) self.ui_data['co2'] = co2 self.tft.fill_rect(110,213,40,16,0) self.tft.draw(self.vector_font, str(co2), 110,221,0xFFFF,0.5) @@ -216,13 +218,16 @@ class Display: w = self.tft.write(self.cn_font, f'{lunar} 星期{w}', 15,135) else: w = self.tft.write(self.cn_font, f'{m:2d}月{d:2d}日 星期{w}', 15,135) - print(f'todo: fill date.tail: {w}') + if w<22*9: # fill + self.tft.fill_rect(15+w,135,22*9-w,22,0) # 处理时间显示 if H != self.ui_data.get('hour'): self.ui_data['hour'] = H + self.tft.fill_rect(25,80,110,48,0) self.tft.write(self.time_font,f'{H:02d}:',25,80,0xF080) if M != self.ui_data.get('minute'): self.ui_data['minute'] = M + self.tft.fill_rect(135,80,90,48,0) self.tft.write(self.time_font,f'{M:02d}',135,80,0xFF80) # 处理建议显示 advice = self.ui_data.get('advice') @@ -230,7 +235,10 @@ class Display: i = self.ticks % len(advice) c = advice[i] w = self.tft.write(self.cn_font, advice[i], 15,45) - print(f'todo: fill advice.tail: {w}') + if w<22*6: # fill + self.tft.fill_rect(15+w,45,22*6-w,22,0) + # 打印内存信息 + _print_mem() # 初始化ui固定元素 def load_ui(self): @@ -238,10 +246,10 @@ class Display: # 默认黑色背景 self.tft.fill(0) # 固定的环境数据图标 - self.tft.jpg("/rom/images/t.jpg",11,177,st7789.SLOW) - self.tft.jpg("/rom/images/rh.jpg",85,177,st7789.SLOW) - self.tft.jpg("/rom/images/pm.jpg",11,209,st7789.SLOW) - self.tft.jpg("/rom/images/ap.jpg",85,209,st7789.SLOW) + self.show_jpg("/rom/images/t.jpg",11,177) + self.show_jpg("/rom/images/rh.jpg",85,177) + self.show_jpg("/rom/images/pm.jpg",11,209) + self.show_jpg("/rom/images/ap.jpg",85,209) # 更新其他默认数据 self.update_ui() diff --git a/utils/download.sh b/utils/download.sh index 9a99d04..4f15d80 100755 --- a/utils/download.sh +++ b/utils/download.sh @@ -1,7 +1,7 @@ #!/bin/sh # 连接并打印文件列表 -mpremote connect /dev/tty.wchusbserial14310 ls +mpremote ls # 生成romfs文件系统并上传 mpremote romfs deploy src/rom