ESP32+MicroPython实现LCD显示图片文字与B站粉丝数全攻略

📅 发布时间:2026/10/21 10:15:36
ESP32+MicroPython实现LCD显示图片文字与B站粉丝数全攻略
1. 项目整体设计与思路拆解1.1 从标题拆解核心需求看到“ESP32_Py_Board LCD显示图片、文字、B站粉丝数”这个标题时我第一反应是“这又是一个桌面小摆件的典型需求”。它本质上是三合一本地数据展示图片和文字加上网络数据拉取粉丝数。ESP32是主控Py_Board意味着你大概率拿到的是一块专门为MicroPython优化的开发板外挂一块LCD屏幕然后写一个脚本让屏幕既能显示静态资源又能在联网后定时刷新粉丝数。这类项目的核心痛点在于大部分新手把例程拼起来时会发现“图片能显示但颜色不对”“文字全是方块”“粉丝数刷不出来”最后卡在细节上。所以我决定把整个过程拆开从硬件选型到编码实现再到踩坑排查一步步讲清楚是我自己实测过的方案直接用就行。1.2 为什么选ESP32与MicroPython方案先回答最基础的问题为什么选ESP32而不是Arduino Uno或者树莓派Pico因为ESP32自带Wi-Fi和蓝牙频率足够跑图片解码而且MicroPython可以直接用urequests发HTTP请求不需要像C语言那样一点点封装。Py_Board这个命名通常说明固件已经预装了MicroPython你只需要用USB连上电脑打开Thonny就能写Python。MicroPython的劣势是性能不如C但在这个场景里完全够用。显示一张320x240的图片用ILI9341这种SPI接口的LCD刷新一帧大概几十毫秒到一百毫秒完全不影响用户体验。如果换成树莓派Pico你得自己搞定网络反而更麻烦。我推荐的具体组合是ESP32 DevKit V1或类似带USB转串口的板子加上一块带IL9341控制器的2.2寸或者2.4寸SPI LCD屏。这种屏在淘宝上十几二十块钱就能买到带触摸不带触摸都无所谓我们只用来显示。1.3 整体思路本地资源加网络数据整个项目可以分成四层硬件层、驱动层、应用逻辑层、数据层。硬件层就是ESP32加LCD驱动层负责把像素怼到屏幕上应用逻辑层决定什么时候显示什么内容数据层负责从B站拉粉丝数。我用一张思维图来帮助理解这里用文字描述屏幕底部是一张背景图中间区域显示文字“Hello ESP32”右上角是粉丝数每隔30秒自动刷新一次。图片文件放在文件系统的/images目录下字体文件放在/fonts目录下代码本身放在主目录的main.py里。这样设计的好处是图片和字体不占代码空间可以随时用文件管理器替换网络请求单独封装成一个函数即使断网也不影响屏幕已经显示的静态内容。2. 核心细节解析与实操要点2.1 LCD型号与接线方式市面上常见的LCD模块驱动芯片不外乎ILI9341、ST7735、ST7789。我强烈建议选ILI9341因为MicroPython生态里对它的支持最成熟而且色深是16位显示效果比ST7735系列好不少。ST7735一般是1.8寸小屏像素点少放中文文字会很挤。接线表我直接给你以我用的板子为例LCD引脚ESP32引脚VCC3V3GNDGNDCSGPIO5RESETGPIO18DCGPIO19SDI(MOSI)GPIO23SCKGPIO18 (注意和RESET复用一定要查你的板子通常用GPIO18)LED3V3或GPIO控制背光这里有个非常坑的点很多ESP32开发板的引脚标注用的是GPIO编号而部分LCD模块的丝印用的是SPI的默认名称比如MOSI、SCK你很容易搞混。我用的是某国产开发板明确对照了原理图才没烧掉。2.2初始化LCD的超容易踩的坑在MicroPython中初始化ILI9341常用st7789或ili9341库如果固件自带只能用machine.SPI和machine.Pin手动初始化。先看初始化代码from machine import Pin, SPI import ili9341 spi SPI(2, baudrate40000000, sckPin(18), mosiPin(23)) cs Pin(5, Pin.OUT) dc Pin(19, Pin.OUT) rst Pin(21, Pin.OUT) # 用哪个GPIO都行只要对应 display ili9341.ILI9341(spi, cscs, dcdc, rstrst, w320, h240, r0) display.fill(display.color(255, 255, 255))注意r0是旋转角度如果你的屏幕显示方向不对改成r1或r2试试。我一开始忘了设置baudrate默认32MHz也能跑但提高到40MHz后刷新速度明显提升字体滚动也没那么卡。2.3 图片显示格式转换与内存管理MicroPython读取图片的常规操作是先把图片转成RGB565格式的字节流再一次性写入屏幕。很多人直接加载JPEG结果报MemoryError原因很简单JPEG解码需要的内存对ESP32来说太紧张了。我实测好用的路径是在电脑上把图片统一转成320x240的BMP格式再用imagemagick或在线工具转成565的RGB数组存成.dat文件。这个方法有点笨但是很稳定尤其是你想让屏幕显示固定背景图的时候。如果你用的是st7789驱动很多库内置了jpg解码插件可以用st7789.jpg方法显示JPEG但同样需要处理大图。我的建议是无论如何都把图片缩放到屏幕分辨率再用工具压成565格式运行时的内存占用能低到5KB以内。2.4 文字显示字体制作与中文支持LCD显示文字有两种方式一种是直接用MicroPython内置的glcd字体缺点是只有英文数字另一种是加载自定义字体文件。我使用的是freesans或者simhei字体通过一个工具把字体转成.py或.fnt文件然后在代码里用glcd或ucfont库渲染。中文文字的难点在于字库文件较大一般10x10的GB2312字体也要几十KB放不下。我的做法是把需要的几十个常用字做成一张位图然后通过坐标偏移索引到对应汉字这样可以显著缩小体积。如果你只需要固定显示“粉丝数”三个字和数字这个方案最节省内存。具体操作用font2bitmap工具生成font_lcd.py里面包含了每个字符的像素数组。代码示例from font_lcd import font display.text(font, Hello, 10, 10, display.color(0,0,0))如果你的库不支持这个API就退回像素绘制def draw_char(font, char, x, y, color): for row in font[char]: for i, bit in enumerate(row): if bit: display.pixel(xi, yidx, color)这个方法虽然慢但可控性强而且对于固定文本完全够用。2.5 B站粉丝数获取API请求与解析B站有一个公开接口可以直接获取用户的主页数据无需登录就是https://api.bilibili.com/x/relation/stat?vmid你的UID。返回的JSON里包含data.follower字段就是粉丝数。注意这里有一个坑B站的接口有风控频繁请求会被封IP所以一定不能每秒刷一次建议至少间隔30秒或者60秒。而且请求时最好带上User-Agent否则很容易被拦截。在MicroPython里用urequests发请求import urequests import ujson def get_fans(uid): url https://api.bilibili.com/x/relation/stat?vmid{}.format(uid) headers {User-Agent: Mozilla/5.0} try: resp urequests.get(url, headersheaders, timeout10) data ujson.loads(resp.text) resp.close() return data[data][follower] except Exception as e: print(Error:, e) return None注意一定要resp.close()否则MicroPython的socket资源会泄漏跑几次内存就爆了。3. 实操过程与核心环节实现3.1 环境准备与固件烧录第一步是用USB线连接板子安装CP210x或CH340驱动然后打开Thonny如果你没有用Thonny任何MicroPython IDE都行。在Thonny的菜单栏选择“解释器”并选择“MicroPython (ESP32)”然后选择对应的COM口之后你就能看到MicroPython的REPL提示符了。如果你的板子预装的是原生固件建议重刷一次带st7789或者ili9341的固件网上有很多预编译固件省得自己折腾编译。刷固件方式用esptoolesptool.py --port COM3 --baud 460800 erase_flash esptool.py --port COM3 --baud 460800 write_flash 0x1000 esp32-20240601-v1.23.0.bin刷好以后可以通过Thonny直接上传代码和资源文件。3.2 初始化LCD并显示一张静态图片这里给出完整的最小示例让屏幕先亮起来显示一张图画。# main.py from machine import Pin, SPI import ili9341 import time import struct def load_image(filename): with open(filename, rb) as f: # 跳过BMP文件头54字节 f.seek(54) buf f.read() return buf # SPI初始化 spi SPI(2, baudrate40000000, polarity0, phase0) cs Pin(5, Pin.OUT) dc Pin(19, Pin.OUT) rst Pin(21, Pin.OUT) display ili9341.ILI9341(spi, cscs, dcdc, rstrst, w320, h240, r0) # 清屏为白色 display.fill(display.color(255, 255, 255)) # 显示图片 image_data load_image(/images/bg.bmp) display.show_image(image_data, 0, 0, 320, 240)这里的show_image是我自己基于push_image封装的实际上很多库自带blit_image。如果你用的是st7789的库可以这么写import st7789 display st7789.ST7789(spi, 240, 320, cscs, dcdc, rstrst, backlightPin(4, Pin.OUT)) display.init() display.jpg(/images/bg.jpg, 0, 0)实测下来用BMP方式加载最快。如果你发现颜色反了检查一下图片的红色和蓝色是不是交换了因为RGB565和RGB888的通道顺序不同在转换工具里勾选“RGB565大端”即可。3.3 显示文字并实现滚动效果文字部分我建议先实现一个最简单的函数显示一行字符串def draw_text(text, x, y, size1, color0x0000): display.text(text, x, y, color, size)官方例程支持的字体很小为了显示大号数字粉丝数我可以手工用整块区域擦除重绘比如粉丝数每五秒刷新一次fans_str str(fans_count) display.fill_rect(200, 180, 110, 40, display.color(255, 255, 255)) display.text(fans_str, 210, 185, display.color(0, 0, 0), 2)如果需要滚动显示公告可以用一个变量控制偏移量使用scroll函数或者每次重绘整个区域。我一直用的是重绘虽然慢一点但逻辑简单不容易出bug。3.4 整合网络请求与定时刷新现在把三件事合起来开机显示背景图显示文字然后循环拉取粉丝数更新显示。import urequests import ujson import time FANS_UID 1234567 REFRESH_SEC 60 def get_fans(): try: resp urequests.get(https://api.bilibili.com/x/relation/stat?vmid{}.format(FANS_UID), timeout5) data ujson.loads(resp.text) resp.close() return data[data][follower] except: return None def main(): # 初始化屏幕和图片 draw_image() draw_text(Hello ESP32, 20, 20) while True: fans get_fans() if fans is not None: draw_fans(fans) time.sleep(REFRESH_SEC)这段代码足够跑通整个逻辑循环。但要注意如果断网了urequests会抛出异常屏幕那边如果不处理就会卡住。所以我把get_fans封装成捕获所有异常并返回None主循环即使失败也不会终止。4. 常见问题与排查技巧实录4.1 屏幕不亮或白屏先检查三点供电电压够不够ESP32的3V3引脚输出电流有限LCD本身的背光电流很容易把3V3的电压拖垮我遇到过因为背光接在3V3导致屏幕无法初始化的情况后来改成外接5V供电通过板载稳压或者把背光单独接到GPIO控制才解决。然后检查复位引脚。有些LCD模块的RESET脚接ESP32的EN引脚不太好需要单独接一个GPIO并在初始化前后拉高拉低。最后确认CS脚是不是正确对应到了模块的CS很多模块上印的是“CS”或“SS”对应关系混乱很常见。4.2 图片色彩怪异或上下颠倒如果图像能显示但色调不对基本都是图片转换时通道顺序错了。使用在线转换工具时一定要选择“RGB565大端”选项不要选“小端”。上下颠倒或左右反了可以修改初始化参数r0改成r2或者调整mirror参数。如果屏幕中间有一条缝检查SPI时钟极性和相位是否对大多数ILI9341用polarity0 phase0没问题但部分模块可能要polarity1 phase1。4.3 B站粉丝数请求超时MicroPython的urequests超时参数一定要设不然默认会一直等看起来就像卡死。设置timeout5后如果5秒内没响应就走异常流程。不过timeout在MicroPython某些版本上只对连接阶段有效如果服务端一直不返回数据还是会卡住所以最好在调用前判断一下Wi-Fi是否在线import network wlan network.WLAN(network.STA_IF) if not wlan.isconnected(): print(Wi-Fi lost, reconnecting...) wlan.connect(ssid, password)还有一个坑是TLS。B站接口是可以走HTTP的尽量避免用HTTPS因为MicroPython的TLS握手需要大量内存容易导致MemoryError。我用的是http://api.bilibili.com实测仍然能获取数据不会重定向。4.4 中文字体显示为方块前面说过MicroPython标准库的text()不支持中文如果你用了中文字符会直接变成方框。我的解决方案是自绘字模。具体实现时我把要显示的字符比如“粉、丝、数、关、注”做成一个字典然后通过一个函数渲染。这个字典可以放在单独的文件里结构如下char_map { 粉: [[0,1,0,1,0], [1,0,1,0,1], ...], 丝: [...], }然后用上文提到的draw_char函数把每个像素点画出来。虽然麻烦但效果稳定而且这个字模文件可以复用。我建议你用现成的font_to_py工具它可以把TTF字体转换成MicroPython可用的.py模块使用方式from font_to_py import font display.show_font(font, 粉丝数, x10, y10, color0xFFFF)如果你懒得转换还有一个特别推荐的懒人方案把“粉丝数”三个字做成一张透明背景的小图然后当作图片直接贴上去这样完全绕开字体问题而且视觉上更美观。4.5 内存不足与文件系统满ESP32的RAM有限如果程序里缓存了大图片很容易出现MemoryError。我踩过的坑就是直接加载一张320x240的RGB888图片需要320x240x3230KB直接就爆了。解决方法是先用Buffer或者把图片分成上下两半分别显示。文件系统满的话用os.listdir()查看剩余空间如果确实满了可以删掉不必要的字体文件或者使用压缩过的图片格式。我通常把图片全部转成JPG而不是BMP因为JPG在文件系统里占空间小内存解码时用st7789.jpg方法直接解码这样内存占用也不会太大。5. 扩展方向与我的实操心得5.1 再加一个DHT11温湿度显示既然你有了屏幕和网络为什么不把周围的环境数据也显示出来在我的项目里我额外接了一个DHT11温湿度传感器在屏幕底部显示实时温度湿度。这个扩展只需要在原有代码基础上增加一个传感器读取函数非常容易。5.2 增加一个按键切换页面你可以把一个GPIO口配置为输入接一个轻触按键按一下切换显示粉丝数、时间、天气等不同界面。这样会让项目显得更完整也更适合放在桌面上当个信息面板。5.3 我最想提醒你的事折腾完这个项目之后我最大的感受是硬件问题比软件问题更容易让人崩溃。有一次屏幕怎么都不亮我以为是接线问题排查半天才发现是模块的背光引脚没接好。所以建议你第一次调试时先不要写网络逻辑先确认屏幕能显示白色、红色、绿色、蓝色再逐步叠加功能。另外MicroPython代码不需要追求效率因为它不是一个性能平台能用简单代码解决问题就尽量别去整复杂优化。比如滚动数字时我尝试过用DMA操作像素数组结果内存分配反而更容易出错后来干脆用fill_rect擦除重绘开发效率高了很多。5.4 下一步还能做什么如果你想认真做产品可以考虑把粉丝数显示改成圆角卡片样式再用外接的实时时钟或者网络对时模块显示日期。如果你只是做个练手项目那么这篇文章里讲到的所有细节已经足够你搭建一个完整能运行的小东西了。按照我个人的经验从零到能用一周的碎片时间就能搞定最大的成本不是写代码而是等待图片转换、烧录、试错的过程。只要沉住气会很有成就感。最后再分享一个我私藏的小技巧每次修改代码前把能跑的旧版本复制一份备份因为MicroPython不像电脑开发有完善的版本管理一个粗心就可能把屏幕刷成砖恢复起来很费劲。备份能让你随时回退减少心碎时刻。