当前位置:首页 > 工业控制 > 电路设计项目集锦
[导读]我有一台M5Stack AtomS3R,放在一个盒子里。它是一个24×24×27毫米的立方体,前面玻璃后面是ESP32-S3芯片,128×128像素的显示屏,整个正面都是一块按钮。内置BMI270六轴IMU和8MB的PSRAM。看起来很可爱,但一直只在闪烁LED灯。

我有一台M5Stack AtomS3R,放在一个盒子里。它是一个24×24×27毫米的立方体,前面玻璃后面是ESP32-S3芯片,128×128像素的显示屏,整个正面都是一块按钮。内置BMI270六轴IMU和8MB的PSRAM。看起来很可爱,但一直只在闪烁LED灯。

这个想法源于我在玩游戏或运行长时间构建时的困扰:我想要一个小小的第二屏幕,用于显示生命值、Discord通知、CPU图表、进度日志,甚至整个显示器画面。这样我就可以把小屏幕粘在主显示屏边缘,快速查看。我觉得如果能做到的话,那为什么不呢?

但把线缆接到这么小的立方体上太傻了。而且它还把东西固定在你的桌面上。AtomS3R有Wi-Fi,所以能不能通过Wi-Fi快速推送实时视频,让画面看起来像直播一样?不是幻灯片或动画那种效果。

这是可能的,但仅因为目标尺寸很小——128x128 是 16,384 像素,大约占 1080p 图像的 0.4%。以质量 65 的 JPEG 格式压缩,大约需要 4 到 5 KB,相当于 3 到 4 个 UDP 数据包。在 30fps 下,这相当于 1.2 Mbps,对 5 GHz 网络来说完全不成问题。整个项目之所以能运行,是因为我从未刻意追求压缩效率,我只是让图像足够小,使得通过 UDP 发送原始 JPEG 已经足够快了。

材料

•M5Stack AtomS3R

•MacOS 或 Windows

•良好的2.4 GHz或5 GHz网络。

软件

•Arduino IDE

•Python 3.9+

•Python 包:'mss'、'pillow'、'pynput',以及可选的 'opencv-python' 和 'numpy'

它是如何工作的

在开始任何设计之前,先了解事物的结构是值得的,因为后续的所有设计决策都源于此。

两个单向重复流。无 TCP,无握手,无协议协商

为什么选择UDP而不是TCP

TCP 保证数据包丢失时仍能送达,但整个过程会暂停直到重传,这在实时视频中完全不适用。如果第47帧丢失了数据包,我并不希望视频流在重新发送期间卡顿200毫秒。等到数据包到达时,第48或49帧可能已经发生了。丢弃的帧是不可见的,而卡顿的视频流则非常明显。

UDP 不会重传,如果收到的数据帧不完整,系统会直接丢弃,下一帧将自动接替。在 30 帧/秒的帧率下,丢失一帧会导致 33 毫秒的延迟,而这种延迟你无法察觉。

为什么选择JPEG而不是原始像素。

一个原始的128x128 RGB565帧为32,768字节。以30帧每秒的速度,这相当于7.9兆比特的UDP传输,但每帧需要24个数据包,只要其中任何一个数据包丢失,整个帧就会失效。使用JPEG 65质量时,同一帧可压缩至4.5KB,减少了4个数据包,意味着丢失几毫秒的可能性大幅降低,解码时间也因此节省下来。

这正是人们容易忽略的反直觉之处:压缩会使数据流更可靠,而不仅仅是更小,因为每个帧中的数据包丢失概率会随着数据包数量增加而累积。

为什么使用8字节头的分块

UDP数据报在技术上可以达到64KB,但通过以太网或Wi-Fi时,超过1500字节的网络MTU的数据包会被IP协议分片处理。如果任一IP分片丢失,整个数据报就会被丢弃。因此,我自行将数据包分片为1400字节,这个大小完全低于MTU,然后由固件负责重新组装。这样,即使某一块数据丢失,也只损失一个帧,而且我可以精确控制整个过程发生的具体情况。

标题为8字节

这就是完整的协议。'frame_id' 通知接收方发送方已进入新的帧。'packet_index' 指明该数据块在缓冲区中的位置。'packet_total' 表示帧何时完成。'payload-len' 处理最后较短的数据块。

闪存固件

安装板支持和库

打开 Arduino IDE

•工具 - 板子 - 板子管理器 - 搜索 'esp32' - 通过 Espressif Systems 安装 esp32

•工具 - 管理库 - 搜索 'M5Unified' - 安装 M5Unified - 它会自动包含 'M5GFX',即负责 JPEG 解码和面板驱动的图形库。

设置您的WiFi凭据

打开“firmware/AtomS3R_Display.ino”文件,并编辑顶部的两行代码;

上传

插入 Atom 并点击上传

了解固件

当你成功设置好固件后,这一步将说明它实际在做什么。

87KB 对于一个微控制器来说太大了,因此被存储在PSRAM中。

64个数据包×1400字节是帧大小的硬上限。实际上,帧通常包含3到5个数据包。预留的缓冲空间使得即使将“JPEG_QUALITY”调高至90以获取更清晰的静态图像,也不会导致文件过大。如果某个帧超出该限制,Python端会拒绝发送该帧,并提示你降低质量,这比神秘的损坏更清晰地表明了问题所在。

数据包处理

这两行验证代码并非出于偏执。这是将套接字绑定到局域网中某个UDP端口的,网络上的任何数据都可能发送垃圾数据,而一个错误的“索引”会导致下方的“memcpy”操作发生越界写入。务必在使用索引前进行验证。

这是“不要等待过时帧”的实现方式。四行代码:一旦新帧的包到达,我们之前半成品的内容就会被丢弃,没有超时调整,也没有缓冲。新的帧ID就是旧帧永远不会完成的信号。

因为除了最后一个块外,每个块的大小都是1400字节,所以目标偏移量就是idx 'CHUNK_SIZE',无需长度表,也无排序要求,数据包可以乱序到达,但最终都会被正确放置。这是一个很不错的特性,可以免费获得。

排空插座

“while”循环至关重要。如果处理器短暂地比原子处理器快,数据包就会在网络栈中排队。如果每次“loop()”迭代只处理一个数据包,那么这个队列就会不断增长,延迟也会逐渐上升,直到无法使用。每经过一次循环都清空所有待处理的数据包,意味着显示屏始终显示最新的完整帧,而任何积压的数据都会立即被清除。

无泪绘画

直接解码到面板时,图像会以每秒30帧的速度从上到下显示可见的撕裂效果。解码为内部RAM中的128x128精灵,因此每一帧都会自动呈现。

主循环

那个条件性的“delay(1)”是一个虽小但有效的优化。当帧在流动时,我们不会进入睡眠状态,而是直接返回到清空套接字的操作。在空闲状态下,我们保持了1毫秒的运行时间,以便让实时操作系统和Wi-Fi栈获得CPU资源。如果无条件地睡眠1毫秒,就会限制可实现的帧率,并增加抖动。

按钮

AtomS3r 的整个前面板玻璃只有一个按钮“BtnA”。M5Unified 为您提供

点击与按住检测免费使用

一个物理输入,两个功能,如此小巧的设备,却要承担整个UI预算。

一句话,容易被忽略

设置电脑直播器

了解Streamer

将 'ATOM_IP' 设置为 atom 显示的绿色部分。这是唯一必须进行的编辑。

屏幕截图

'mss' 是负责抓取的库,它是在每个操作系统原生截图 API 上的轻量级封装,其快速的 600x600 图像处理只需几毫秒。而 'PIL.ImageGrab' 和 'pyautogui' 都要慢得多,会成为瓶颈。

默认行为是显示器上输入的最大正方形区域。由于屏幕为方形,因此捕捉一个正方形区域可避免失真。若要镜像特定窗口,请设置其坐标。

放大模式

一个200x200的方框,当光标移动时会放大到128x128的面板上,通过硬件循环嵌套的“最大”和“最小”夹子将其固定在显示器上,这样当光标位于屏幕角落时,就不会尝试获取屏幕边缘的像素。

这最初是作为调试工具,后来却成了真正最有趣的模式。

编码

OpenCV 是可选的,但强烈推荐使用。它比 Pillow 快 3-5 倍,这一点在你设定 30fps 时尤为重要。脚本会检测到导入时间并自动选择路径,因此仅使用 Pillow 的设置仍然有效。

分块和发送

这里有一个小细节,处理起来花了我一段时间。

画面节奏

绝对截止时间的节拍控制,而不是每次迭代都使用“sleep(1/30)”固定间隔睡眠,会导致漂移,因为捕获编码工作并非免费且也不恒定。

运行

原子应该在大约一秒钟内点亮你的屏幕。

结果

在 Mac 上进行了端到端测试,效果与承诺的数字大致相当。第一次运行时触发了 macOS 的屏幕录制提示,Atom 程序随即亮起,并在第二轮中显示了屏幕内容。

5 GHz 上的稳态

真正的考验是让它开着,用CS2玩一局,原子从屏幕中间镜像出一角。整个游戏帧率稳定,画面在快速的摄像机摆动中依然流畅,而小面板上的文字也勉强能看清。

还没完成,不过我已经测试得相当不错了。游戏旁边的迷你显示器,功能正常。128x128的限制让整个系统易于构建,也使得它在完全符合我想要测试的内容时,具有极佳的可读性。

本文编译自hackster.io

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

你是否好奇过微控制器如何同时处理多个任务?本项目展示了STM32F407ZGT6 RT-Spark开发板上定时器中断和任务调度的工作原理。两个定时器以不同的频率闪烁LED,而按钮中断则在按下时立即响应——所有这些功能都通...

关键字: 微控制器 开发板 LED STM32CubeMX

该项目是一个简单的人体反应计时器,使用RT-Spark开发板和STM32F407ZGT6微控制器构建而成。它用于测量当RGB LED亮起时,人能多快做出反应。

关键字: 微控制器 开发板 LED STM32F407ZGT6

我制作了一个人类反应计时器,用来精确测量一个人对灯光亮起的反应速度。电路板会点亮内置的RGB LED灯,我的任务是尽可能快地按下板上的按钮(SW1)。

关键字: LED 电路板 示波器

引言:微控制器广泛应用于嵌入式系统中,用于控制输入和输出设备。GPIO引脚使微控制器能够与所使用的外部硬件进行通信,例如开关按钮、LED灯和显示屏(LCD)。

关键字: 微控制器 嵌入式系统 LED

这是我对硬件与电子世界的入门介绍。Arduino UNO上的GPIO引脚(D0 - D13)用于控制连接到它们的元件,这里就是LED灯。通过编写代码,可以产生电信号来控制LED灯的开关。

关键字: LED 面包板 GPIO引脚 Arduino UNO

本项目详细介绍了采用离散模拟和功率电子元件构建的自动应急备用LED照明系统。该电路将环境光感应与自动双电源切换(交流电源 + 3.7V 18650锂离子电池备用)无缝结合。

关键字: LED 功率电子 二极管 LM7805

空气质量问题不仅限于户外,室内空间也可能积聚挥发性有机化合物(VOCs)、二氧化碳(CO₂)和污染物,影响健康与工作效率。本项目将XIAO RP2040和ENS160传感器改造为一款桌面型空气质量监测仪。设备内置一个JU...

关键字: 传感器 LED XIAO RP2040

火焰无声,但我们可以通过简单的电子元件为它们“装上耳朵”!这个DIY火灾报警器项目利用红外接收传感器检测火光辐射,并立即触发声音(蜂鸣器)和视觉(LED)报警。该电路以13009 NPN晶体管作为主开关,结构简单、可靠且...

关键字: 蜂鸣器 LED 传感器 1N4007二极管

如果我们用Arduino核心在ESP8266上写任何代码,很可能已经遇到了这个瓶颈。你写的每一行代码都运行在loop()函数内部,而loop()是协作式的——它从上到下依次执行,只有在完成所有操作后,SDK才会有机会进行...

关键字: Arduino CPU LED ESP8266

因此我制作了这个小巧的终端模拟器。它使用的是Makerfabs ESP32-S3并行TFT触摸屏,配备3.5英寸ILI9488显示屏。一款专为无头Linux设备设计的物理串行控制台,基于Makerfabs ESP32-S...

关键字: 触摸屏 服务器 显示屏 ESP32-S3
关闭