我们重视您的隐私
通过点击「允许所有 Cookie」,代表您同意在您的设备上存储 Cookie 以增强网站浏览体验、分析网站使用情况并协助我们的营销和网站效能优化工作。您可以在我们的隐私权政策中找到有关于此的更多信息。
上一篇技术文章“Smart Display RS485 Introduction”对 RS485 和 Modbus 协议进行了初步介绍。本文将通过多页面示例进一步介绍几种常用的 Smart Display 开发技巧,包括:
本项目包含三个页面,可通过 Button widget 在页面之间进行切换。各页面的内容和功能如下:
页面 1:
右侧设有一个用于切换至下一页的按钮,其下方设有一个 Horizontal Slider,用于调节屏幕亮度。预览图如下:

页面 2:
左侧设有返回上一页的按钮,右侧设有切换至下一页的按钮。此外,下方还有三个按钮,可分别产生短、中、长三种不同时长的蜂鸣提示音。

页面 3:
左侧设有返回上一页的按钮。使用可变电阻模拟模拟量输入,并将结果显示在 Graph widget 上。

由于本示例使用了 RS485 扩展板,在开始编程前,请通过库管理器(Library Manager)下载所需的库:

首先,使用 GUI Builder 创建页面内容。每个页面中的 Text widget 都可以直接输入文字,因此无需在程序中另外定义。

各 widget 的详细内容如下表所示:

您可以根据现有材料选择 Smart Display 型号,并使用不同的 widget 创建不同的界面。需要注意的是,每个页面的 widget ID 和 type 必须与程序中的定义保持一致。为避免增加复杂度,建议根据此表提供的 widget ID 和 type 创建页面内容。
接下来进入程序部分。
下载 ModbusMaster 库后,请确保使用 #include 指令将其引入:

此外,我们还将一些与 Smart Display 相关的常量放在 SmartDisplayModbus.h 中,以便统一管理。请确保同时将该文件引入。
上一篇文章介绍了 widget ID 与寄存器地址的转换方法,如下所示:

不过,该表格是基于早期每个页面最多包含 10 个 widget 的设计。目前,每个页面最多可包含 64 个 widget。该表格仍适用于前 10 个 widget,但从第 11 个 widget 开始,需要切换到新的寄存器区域。因此,我们定义了以下宏:

为了便于使用,我们定义了一个根据 widget ID 进行读写的函数,并在函数中完成寄存器地址转换:

使用 GUI Builder 设计页面内容时,我们已经提到 widget ID 必须保持一致。因此,程序中定义了以下 widget ID 常量:

使用这些常量定义有助于识别 widget 类型,并方便与实际页面设计进行对照。如果后续修改 GUI 设计,请记得核对这些数值,以避免出现错误。
固件通过计数器延迟一段时间后向蜂鸣器发送 0/1 信号使其发声,并使用其他计数器控制声音的启动和停止时间。为了使蜂鸣器发声,需要写入以下四个寄存器:

为了调整声音持续时间,我们创建一个传入 SMARTDISPLAY_BUZZER_HIGH 参数的函数来控制蜂鸣器发声。示例如下:

只有当 SMARTDISPLAY_BUZZER_ACTIVE 的值与上一次写入的值不同时,蜂鸣器才会发声。因此,该变量在函数中以 static 形式保存,并在每次调用前进行反向处理。
最后,在页面处理函数中,即可根据按钮状态产生不同时长的蜂鸣声:

切换页面前,需要先进入 CONFIG_MODE。设置新页面后,再切换至 DISPLAY_MODE。切换页面时,Smart Display 会重新加载新页面上的所有 widget。为了避免在 Smart Display 完成加载前进行读写操作,通常需要加入一段延迟时间。延迟时间的长短取决于页面的复杂程度。如果页面切换后出现异常,可以尝试适当延长延迟时间,确认问题是否得到解决。

页面切换后,新页面上的 widget 会恢复为默认值。因此,我们建立了一套处理流程,用于执行页面切换、记录 widget 数值以及恢复 widget 数值。
首先,定义用于页面处理的函数指针,以便建立页面表,并为每个页面指定对应的处理函数:

接着建立页面表:

常量 MAX_WIDGETS_PER_PAGE 定义了每个页面需要记录数值的 widget 数量。如果后续增加了 widget,请记得同步更新该值。
最后,在 loop() 函数中处理页面切换和 widget:

如果当前页面 _curPage 与下一页面 _nextPage 不同,则将页面切换标志 newPage 设为 true。这样页面处理函数就会知道需要进行初始化。下面以 Page0 为例:

如果传入的 init 参数为 true,表示刚进入该页面,我们会将 WIDGET_P0_BUTTON_NEXT 的值设为 0。这是因为切换到新页面时,按钮的默认值为 0。我们希望只有在按钮被按下时(即数值从 0 变为 1)才执行特定指令(本例中为切换至 PAGE_1)。因此,需要将其重新设为 0,因为该值之前可能已经因按钮被按下而变为 1。
Slider widget 加载时的初始值同样为 0,但之前可能已经通过该 widget 调整过亮度,因此需要将其恢复为当前的亮度值 _brightness。
在其他情况下(init 为 false),可以根据 widget 的数值是否发生变化进行相应处理。最后,将所有 widget 的数值保存下来,供下一次处理时参考。
对于下一页面 _nextPage 的设置,可以使用以下函数:

页面处理函数会检查 widget 的内容。如果检测到按钮数值从 0 变为 1,即可调用该函数发出页面切换请求。示例如下:

Graph widget 可以将写入的数值以图形方式显示。在本示例中,我们将可变电阻连接至 A0 来模拟模拟量输入,并通过 Graph widget 显示数据变化。Arduino 的模拟输入范围为 0-1023,而 Graph widget 接受的数值范围为 0-100,因此需要使用 map() 函数进行范围转换。
Graph widget 仅在数值发生变化时更新画面。如果希望实现周期性更新效果,可以采用一个简单的方法:定期检查数值,如果新的数值与上一次读取的数值相同,则对其进行微调(加 1 或减 1)。这样即可产生周期性更新的动态显示效果。

以上就是本次示例程序的说明,希望能够帮助读者进一步了解相关的开发方法。本示例也可作为后续项目开发的基础框架,有助于缩短开发时间。完整程序可通过以下链接下载:Modbus-MultiPageOperation on GitHub。
如果您对 RS485 / Modbus 还不熟悉,可以参考上一篇文章了解更多信息:Smart Display RS485 Introduction。
通过点击「允许所有 Cookie」,代表您同意在您的设备上存储 Cookie 以增强网站浏览体验、分析网站使用情况并协助我们的营销和网站效能优化工作。您可以在我们的隐私权政策中找到有关于此的更多信息。