基于ESP32与Visuino的物联网笑话生成器:图形化编程实践
1. 项目概述与核心思路
最近在捣鼓M5Stack Core2,想做个能联网的小玩意儿,既要有趣,又能把物联网里最核心的“从网上拿数据”这个流程跑通。最后决定做个“随机笑话生成器”,按一下按钮,设备就从网上抓个笑话回来,分两步显示在屏幕上。这听起来简单,但里面包含了物联网项目从硬件选型、网络连接到数据解析、人机交互的完整闭环。我选择了Visuino这个图形化编程工具来快速实现,整个过程就像搭积木,不用写大段代码,特别适合想快速验证想法或者刚接触嵌入式开发的朋友。这个项目麻雀虽小,五脏俱全,能让你在几个小时内就摸清ESP32联网、调用API、处理JSON数据的基本门道。
2. 硬件与软件环境准备
2.1 硬件选型:为什么是M5Stack Core2?
手头用的是M5Stack Core2开发套件。选它有几个很实际的理由。首先,它核心是一颗ESP32-D0WDQ6芯片,双核240MHz,性能对付这种网络请求和屏幕刷新绰绰有余,最关键的是它集成了Wi-Fi和蓝牙,省去了外接模块的麻烦。其次,它自带一块2英寸的IPS电容触摸屏,分辨率是320*240,显示文字和简单UI足够清晰,而且屏幕、主板、电池都集成在一个精致的小盒子里,开箱即用,不用自己再去焊接屏幕、连接排线,极大地降低了硬件搭建的门槛和出错概率。对于快速原型开发来说,时间就是金钱,这种高度集成的硬件能让你把精力集中在逻辑实现上。
注意:如果你手头没有M5Stack Core2,用其他带屏幕和Wi-Fi的ESP32开发板(比如TTGO T-Display)理论上也可以,但需要根据具体板型在Visuino中选择对应的组件并调整引脚连接,过程会稍微复杂一些。
2.2 软件工具:Visuino图形化编程的优势
软件方面,核心工具是Visuino。这是一个基于图形化界面的Arduino开发环境。对于这个项目,它的优势非常明显。传统的嵌入式开发需要编写C/C++代码来配置Wi-Fi、发起HTTP请求、解析JSON,每一环都可能遇到语法错误、库依赖等问题。Visuino把这些功能都做成了可视化的组件,比如“WiFi接入点”、“TCP/IP客户端”、“HTTP客户端”、“JSON解析器”等。我们只需要从工具箱里把这些“积木”拖到设计区,然后用“线”把它们按照逻辑关系连接起来就行。它背后会自动生成对应的Arduino代码,我们只需要关心逻辑流,不用纠结于具体的函数调用和语法细节,开发效率提升不是一点半点,尤其适合教育、快速原型和那些更关注功能而非代码细节的应用场景。
3. 项目核心原理与流程拆解
3.1 系统工作流程全景图
别看这个笑话生成器功能简单,其背后的数据流和状态切换构成了一个清晰的物联网应用模型。整个系统可以抽象为以下几个核心阶段:
- 初始化与待机:设备上电,程序初始化,屏幕显示初始界面(或空白),设备处于低功耗待机状态,等待用户触发。
- 用户触发与网络连接:用户按下按钮A(对应M5Stack Core2的左下角触摸按钮)。这个动作触发一个信号,命令设备启动Wi-Fi模块,并尝试连接到预设的无线网络。连接成功后,设备会获得一个本地IP地址,具备了访问互联网的能力。
- HTTP请求发起:网络就绪后,系统立即向指定的笑话API服务器(
official-joke-api.appspot.com)发起一个HTTP GET请求。这个请求的目的地是/random_joke这个路径,意思就是“请给我一个随机的笑话”。 - 数据接收与解析:API服务器处理请求,并从它的笑话数据库中随机选取一条,以JSON格式打包,通过HTTP响应发回给我们的ESP32。ESP32收到的是一个文本字符串,其内容类似
{"type":"general","setup":"Why did the chicken cross the road?","punchline":"To get to the other side!"}。接下来,我们需要用JSON解析器把这个字符串“拆开”,提取出setup(笑话的铺垫/问题)和punchline(笑点/答案)这两个关键字段。 - 内容分步显示与交互:解析出的
setup部分会立刻显示在屏幕上。此时,笑话只讲了一半,吊足胃口。然后系统再次进入等待状态,等待用户第二次交互。当用户按下按钮B(右下角触摸按钮)时,系统才将之前解析好的punchline部分推送到屏幕,完成整个笑话的展示。 - 连接管理与复位:显示完成后,系统会延迟一段时间(例如2秒),然后主动断开TCP连接,以节省网络资源和电量。之后,设备恢复到初始的待机状态,准备开始下一次“按按钮-取笑话”的循环。
这个过程完美诠释了物联网终端设备的典型交互模式:本地触发 -> 云端获取 -> 数据处理 -> 本地呈现。
3.2 关键技术点深度解析
HTTP/HTTPS与API调用:我们通过HTTP协议与远程服务器通信。HTTP GET是最常见的请求方法,用于“获取”资源。这里的资源就是那个随机的笑话。虽然有些API要求使用更安全的HTTPS,但本例中的官方笑话API使用的是HTTP,这在Visuino中用标准的TCP/IP客户端组件就能处理。理解这一点很重要,因为如果换用需要HTTPS的API(比如很多天气API),你可能需要寻找支持SSL的组件或采用其他方法。
JSON数据格式及其处理:JSON(JavaScript Object Notation)是网络数据交换的“通用语言”,轻量且易读。它由键值对组成。我们的目标就是从返回的一大段JSON文本中,精准地找到setup和punchline这两个“键”所对应的“值”。在Visuino中,“Split JSON Object”组件就是干这个的。你需要提前告诉它JSON的结构(通过解析一个样本),它就能在运行时像一把智能钥匙,自动打开数据包,取出你想要的部分。
事件驱动编程模型:整个程序不是顺序执行的死循环,而是基于“事件”的。比如“按钮被按下”是一个事件,“Wi-Fi连接成功”是一个事件,“HTTP数据接收完成”也是一个事件。Visuino的连线操作,本质上就是在定义“当A事件发生时,去触发B动作”。这种模型非常贴合物联网应用场景,使得程序能够高效地响应各种内外部触发条件,而不是盲目地轮询。
4. Visuino详细配置与实操步骤
4.1 工程初始化与板卡设置
首先,打开Visuino软件。在中间的主设计区域,你会看到一个默认的“Arduino”组件。这是我们所有操作的起点。用鼠标点击它,然后在右侧的属性面板中找到“Board”或类似的选项。点击旁边的“...”按钮或下拉菜单,会弹出一个庞大的板卡支持列表。在这里面找到并选择“M5Stack Core2”。这个步骤至关重要,它确保了Visuino后续生成的代码能正确调用M5Stack Core2的专用库(如M5Core2.h),并映射正确的引脚功能(特别是屏幕和触摸按钮)。
4.2 网络连接配置:让设备“上网”
物联网项目,网络是第一步。在左侧的组件工具箱中,依次展开Modules->WiFi->Access Points。点击Access Points旁边的“...”按钮,会打开一个管理窗口。从右侧的可用组件列表中,将一个“WiFi Access Point”拖拽到左侧的已用组件区。选中这个新拖入的组件,在右侧属性面板中设置两个关键参数:
- SSID:这里填入你的无线网络名称(Wi-Fi名字)。
- Password:这里填入该Wi-Fi的密码。 配置好后关闭这个窗口。这样,我们就创建了一个Wi-Fi连接配置模块。
接下来,配置网络套接字。同样在工具箱,找到Modules->WiFi->Sockets,点击“...”打开套接字管理窗口。从右侧拖一个“TCP/IP Client”到左侧。选中它,在属性面板中设置:
- Host:填入笑话API的服务器地址
www.official-joke-api.appspot.com。这就是设备要连接的目标。 - Port:填入端口号
80。HTTP协议默认使用80端口。 这里有个细节:我们虽然进行的是HTTP通信,但在底层网络层面,Visuino使用了一个TCP客户端来建立可靠的连接通道。HTTP协议是构建在TCP之上的。
4.3 用户界面组件布局
我们的输出设备是屏幕。选中主设计区的M5Stack Core2板卡组件,在属性面板中找到Modules->TFT Display->Elements,点击旁边的“...”按钮,打开显示元素编辑器。
- 首先,拖入一个“Fill Screen”元素。它的作用是在每次更新文字前清空屏幕,避免新旧文字重叠,造成显示混乱。你可以设置它的填充颜色,默认黑色即可。
- 然后,拖入一个“Text Field”元素。这就是用来显示笑话文本的区域。选中它,在属性面板中调整关键属性:
- Size:设置为3。这是文字大小,根据你的屏幕分辨率和喜好调整,3在M5Stack Core2上看起来比较舒适。
- Wrap:设置为
True。这意味着当一行文字太长时,会自动换行。对于笑话这种可能较长的文本,这个设置必须打开,否则文字会跑出屏幕外。 - Y:设置为10。这是文本区域距离屏幕上边缘的起始像素点。设为10可以给顶部留出一点边距,显示更美观。 配置完成后关闭元素编辑器。
4.4 逻辑功能组件添加与连接
现在是搭建项目“大脑”的时候了。我们需要从左侧的组件工具箱中,将以下逻辑组件逐一拖到主设计区:
- Debounce Button (x2):用于处理按钮信号。物理按钮在按下时会产生机械抖动,这个组件能过滤抖动,确保一次按压只触发一次有效事件。我们分别命名为Button1(对应按钮A)和Button2(对应按钮B)。
- HTTP Client:这是发起HTTP请求的核心组件。
- Delay:用于实现延时操作,比如在显示笑话后延迟断网。
- Char To Text:网络接收的数据是逐个字符(Char)的流,这个组件负责把字符流重新组合成完整的文本(String)。
- Split JSON Object:JSON解析器,用来拆分数据。
- Text On/Off Switch:一个文本开关。我们用它来控制“笑点”部分的显示时机。只有打开它,笑点文本才能通过。
- Text Multi-Source Merger:文本合并器。它有两个输入源,可以将“铺垫”和“笑点”按顺序合并,然后一次性输出给屏幕显示组件。
接下来是最关键的步骤:用“线”(代表数据流或事件流)连接所有组件。请严格按照以下顺序和逻辑进行连接,你可以理解为在绘制一张数据流向图:
建立连接与触发请求:
- 将
M5Stack Core2板卡组件下的TCP Client1引脚[Out]连接到Delay1组件的[Start]引脚。这表示TCP客户端一旦有输出(比如连接建立),就启动延时器。 - 将
Delay1的[Out]引脚连接到TCP Client1的[Disconnect]引脚。这表示延时结束后,断开TCP连接。 - 将
Delay1的[Out]引脚同时连接到CharToText1的[Clock]引脚。延时结束后,也触发字符到文本的转换完成。 - 将
M5Stack Core2的[Remote Connected]引脚连接到HTTPClient1下GET1请求的[Clock]引脚。这确保了只有在网络真正连接成功后,才触发HTTP GET请求。 - 将
Button1的[Out]引脚也连接到HTTPClient1 > GET1的[Clock]引脚。这样,用户按下按钮A也能直接触发获取笑话的请求。
- 将
硬件输入绑定:
- 将
M5Stack Core2->TFT Display->Touch->Button A的[Out]引脚连接到Button1组件的[In]引脚。这将硬件触摸按钮A的事件绑定到我们的去抖动按钮逻辑组件。 - 同理,将
Button B的硬件引脚连接到Button2的[In]引脚。
- 将
HTTP请求与响应处理:
- 将
HTTPClient1的[Out]引脚连接到M5Stack Core2->WiFi->TCP Client1的[In]引脚。这建立了HTTP客户端与底层TCP连接之间的通道。 - 将
HTTPClient1->GET1->Response->Content的[Out]引脚连接到CharToText1的[In]引脚。这里流动的就是从服务器返回的原始笑话数据(JSON字符串)。
- 将
JSON解析与内容分流:
- 将
CharToText1的[Out]引脚连接到SplitJSON1的[In]引脚。完整的JSON文本被送入解析器。 - 将
SplitJSON1的setup输出引脚连接到MultiMerger1的输入引脚[0]。笑话的“铺垫”部分直接进入合并器的第一个通道。 - 将
SplitJSON1的punchline输出引脚连接到Switch1的[In]引脚。笑话的“笑点”部分先进入一个开关。
- 将
交互控制与最终显示:
- 将
Button2的[Out]引脚连接到Switch1的[Enable]引脚。这意味着只有用户按下按钮B,这个开关才会打开,允许“笑点”通过。 - 将
Switch1的[Out]引脚连接到MultiMerger1的输入引脚[1]。此时,笑点文本进入合并器的第二个通道。 - 最后,将
MultiMerger1的[Out]引脚,同时连接到三个地方: a.M5Stack Core2->TFT Display->Fill Screen1的[Clock]引脚(触发清屏)。 b.M5Stack Core2->TFT Display->Text Field1的[In]引脚(输入要显示的文本)。 c.Text Field1的[Clock]引脚(触发屏幕更新显示)。
- 将
实操心得:连线时一定要耐心,确保箭头的起点(输出引脚)和终点(输入引脚)正确。Visuino的连线有“吸附”效果,接近正确引脚时会高亮,这是一个很好的辅助。复杂的项目可以分模块连线,例如先把网络相关组件连好,再连数据处理部分,最后连显示部分,避免线缆杂乱。
4.5 组件参数微调
连好线后,还需要对一些组件的属性进行精细设置:
- 选中
Switch1,在属性面板中找到Send On Enable,将其设置为True。这保证了开关在“打开”的瞬间,会将当前输入的内容发送出去。如果设为False,则只在开关状态变化时发送,可能不符合我们的需求。 - 选中
CharToText1,设置Max Length为300。这限制了它接收的最大字符数,防止因异常数据导致内存溢出。一个笑话的JSON文本通常不会超过300字符。 - 选中
Delay1,设置Interval为2000000。这里的单位是微秒(μs),2000000μs就是2秒。这个延时决定了笑话显示完成后,多久断开网络连接。 - 选中
HTTPClient1,确认Host属性已正确设置为www.official-joke-api.appspot.com。 - 双击
HTTPClient1组件,会打开其内部结构。里面应该已经有一个GET1请求(如果没有就拖一个进去)。选中这个GET1,在属性面板中设置URL为/random_joke。这就是我们向服务器请求的具体资源路径。
4.6 JSON解析器训练
为了让Split JSON Object组件知道如何解析数据,我们需要“教”它。右键点击SplitJSON1组件,选择“Parse JSON Array...”或类似选项。会弹出一个窗口,要求你提供一个JSON样本。你可以直接访问https://official-joke-api.appspot.com/random_joke,把浏览器里显示的完整内容复制过来,粘贴进这个窗口。样本看起来是这样的:
{"type":"general","setup":"Why did the chicken cross the road?","punchline":"To get to the other side!"}点击“Parse”或“OK”。Visuino会分析这个样本,自动识别出type、setup、punchline这三个字段,并在组件上生成对应的输出引脚。这样,在程序运行时,它就能准确地将收到的同类JSON数据拆分开来。
5. 代码生成、编译与上传
所有图形化配置完成后,就来到了最后一步——生成并上传代码。点击Visuino界面下方的“Build”标签页。首先,在“Port”下拉菜单中选择你的M5Stack Core2所连接的串口(如果没找到,检查USB线是否接好,板卡是否通电)。然后,点击“Compile/Build and Upload”按钮。
Visuino会依次执行以下操作:
- 代码生成:根据你的图形化设计,在后台生成等效的Arduino C++代码。这个过程是自动的,你可以通过点击“Code”标签页查看生成的代码,非常直观。
- 编译:调用Arduino IDE的编译器,将生成的代码以及所有用到的库文件编译成ESP32可执行的机器码。
- 上传:通过串口,将编译好的程序烧录到M5Stack Core2的闪存中。
上传过程中,M5Stack Core2的屏幕可能会闪烁,串口监视器会显示进度信息。看到“上传成功”的提示后,设备会自动重启。
6. 功能测试与效果验证
给设备上电(如果USB供电则已通电)。屏幕初始可能是空白或带有M5Stack的Logo。此时,用手指点击屏幕左下角的虚拟按钮区域(对应Button A)。你应该能看到:
- 屏幕可能清空一下。
- 稍等片刻(网络连接和请求需要时间),一段英文笑话的“铺垫”部分会显示在屏幕上。例如:“Why did the chicken cross the road?” 此时,笑话只讲了一半。接着,点击屏幕右下角的虚拟按钮区域(对应Button B)。屏幕上会立刻追加显示该笑话的“笑点”部分。例如:“To get to the other side!” 至此,一个完整的“获取-显示”交互流程就成功了。你可以多次点击按钮A来获取新的随机笑话。
7. 常见问题排查与进阶优化
7.1 问题排查速查表
在实际操作中,你可能会遇到一些问题。下面是一个快速排查指南:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 屏幕无任何反应,按钮无效 | 1. 程序未成功上传 2. 板卡型号选择错误 | 1. 检查Visuino“Build”页面的上传日志,确认无错误且显示“上传成功”。 2. 确认主设计区板卡组件属性中,选择的确实是“M5Stack Core2”。 |
| 点击按钮A,屏幕闪烁但无文字 | 1. Wi-Fi连接失败 2. API服务器无法访问 | 1. 检查WiFi Access Point组件中的SSID和密码是否正确,确保设备在Wi-Fi信号范围内。2. 尝试用电脑浏览器访问 http://official-joke-api.appspot.com/random_joke,看是否能返回JSON数据。检查网络防火墙或代理设置。 |
| 显示乱码或残缺文本 | 1. 文本字段属性设置不当 2. JSON解析失败 | 1. 检查Text Field组件的Wrap属性是否设为True,Size是否合适。2. 检查 CharToText1的Max Length是否足够大(如300)。右键点击SplitJSON1,重新提供一份正确的JSON样本进行解析训练。 |
| 只显示“铺垫”,按按钮B无“笑点” | 1. Button2连线错误或未绑定 2. Switch1组件未启用或配置错误 | 1. 检查Button B硬件引脚是否正确连接到Button2组件的[In],以及Button2的[Out]是否连接到Switch1的[Enable]。2. 确认 Switch1组件的Send On Enable属性设置为True。 |
| 获取笑话速度很慢 | 1. 网络延迟 2. API服务器响应慢 | 1. 属于正常现象,取决于你的网络环境和API服务器状态。可考虑在代码中增加“正在加载…”的提示。 2. 可以尝试在 Delay1组件后增加一个短暂的延时再触发下一次请求,避免频繁请求。 |
7.2 项目扩展与优化思路
这个基础项目就像一个乐高底座,有巨大的扩展潜力:
- 增加笑话分类:官方笑话API支持按类别获取,如
/jokes/programming/random。你可以在Visuino中增加多个按钮,每个按钮触发请求不同的URL路径,实现“编程笑话”、“冷笑话”等分类选择。 - 自动轮播与定时刷新:去掉按钮触发,改用“Timer”组件。设置一个间隔(比如30秒),让设备自动获取并显示新笑话,实现一个桌面电子相框式的笑话展示器。
- 本地存储收藏:M5Stack Core2有闪存。可以增加一个“收藏”按钮(如屏幕中间按钮C)。当显示一个喜欢的笑话时,按下它,将当前的
setup和punchline保存到文件系统(如SPIFFS)。再增加一个“浏览收藏”模式,循环显示保存的笑话。 - 多语言与翻译:调用翻译API(如Google Translate API或国内可用的翻译服务)。流程变为:获取英文笑话 -> 发送到翻译API -> 接收并显示中文翻译。这涉及到串联两个API,是更复杂的网络交互练习。
- 美化用户界面:利用Visuino的显示元素,添加背景图片、更换字体颜色和大小、为“铺垫”和“笑点”设计不同的显示样式(如不同颜色、位置),甚至加入简单的显示动画。
- 改用其他API:掌握了核心方法后,你可以轻松替换API。比如,将笑话API换成天气API(如OpenWeatherMap),按钮A显示城市和温度,按钮B显示天气状况和湿度,就变成了一个简单的桌面天气站。关键步骤是:修改HTTP请求的Host和URL;根据新API的JSON响应格式,重新训练JSON解析器;将解析出的字段(如
main.temp、weather[0].description)连接到显示组件。
这个基于ESP32和Visuino的随机笑话生成器项目,其价值远不止于显示几个笑话。它提供了一个极其清晰、可视化的模板,展示了物联网设备如何与云端服务对话。图形化编程降低了门槛,让你能聚焦于逻辑和功能设计。当你成功运行起第一个项目后,那种“软硬件结合、设备联网、数据流动”的成就感,会激励你去探索更多可能。动手试试吧,从修改一个参数、替换一个API开始,你会发现创造自己的智能硬件应用,并没有想象中那么困难。
