+
+✅ 订阅 color_topic(uuid)
主题
+✅ 在单独的终端里运行 host_client
。host_client
大约每秒会发布一个开发板 LED 的颜色 color
。
+✅ 通过记录从这个主题收到的信息,来验证订阅是否有效。
+✅ 对 LED 命令作出响应:用 led.set_pixel(/* 收到的颜色 */)
函数把新收到的颜色设置到板子上。
+intro/mqtt/exercise/solution/solution_publ_rcv.rs
包含解答。你可以用下面的命令运行它:
+cargo run --example solution_publ_rcv
+
+
+开发板 LED 命令包含三个字节,分别表示红、绿、蓝。
+
+enum ColorData
包含一个主题 color_topic(uuid)
和 BoardLed
+- 可以使用
try_from()
来转换 EspMqttMessage
的 data()
字段。首先需要用 let message_data: &[u8] = &message.data();
将消息强制转换为 slice
+
+
+#![allow(unused)]
+fn main() {
+// RGB LED 命令
+
+if let Ok(ColorData::BoardLed(color)) = ColorData::try_from(message_data) { /* 在这里设置新的颜色 */ }
+}
+
+
+EspMqttClient
不止负责发布消息,也用于订阅主题。
+
+#![allow(unused)]
+fn main() {
+let subscribe_topic = /* ... */;
+client.subscribe(subscribe_topic, QoS::AtLeastOnce)
+}
+
+
+处理函数闭包里的 message_event
参数的类型是 Result<Event<EspMqttMessage>
。
+因为我们只对接收成功的消息感兴趣,我们可以在闭包里使用模式匹配:
+
+#![allow(unused)]
+fn main() {
+let mut client =
+ EspMqttClient::new(
+ broker_url,
+ &mqtt_config,
+ move |message_event| match message_event {
+ Ok(Received(msg)) => process_message(msg, &mut led),
+ _ => warn!("Received from MQTT: {:?}", message_event),
+ },
+ )?;
+}
+
+在处理函数中,我们将会处理 Complete
消息。
+💡 使用 Rust Analyzer 来生成缺失的 match 分支,或者匹配所有其他类型,输出一个 info!()
。
+
+#![allow(unused)]
+fn main() {
+match message.details() {
+ // 本练习中的消息都会是 `Complete` 类型的
+ // `Details` 枚举的其他变体用于更大的消息 payload
+ Complete => {
+
+ // Cow<&[u8]> 可以被强制转换为 slice &[u8] 或 Vec<u8>
+ // 你可以将它强制转换为 slice ,用 try_from() 发送
+ let message_data: &[u8] = &message.data();
+ if let Ok(ColorData::BoardLed(color)) = ColorData::try_from(message_data) {
+ // 把 LED 配置为新收到的颜色
+
+ }
+ }
+ // 使用 Rust Analyzer 来生成缺失的 match 分支,或者匹配非 complete 的消息来输出日志消息。
+}
+}
+
+💡 用 logger 来查看接收到的东西,例如:info!("{}", color);
或 dbg!(color)
。
+
+
+✅ 如果你已经完成了所有其他工作,可以考虑实现这个任务。我们不提供完整的解答,因为这是用于测试你自己能走多远。
+检查 common/lib/mqtt-messages
:
+✅ 使用分层主题的 MQTT 实现相同的功能。订阅所有的“命令”消息,在 cmd_topic_fragment(uuid)
后面加一个 #
通配符。
+✅ 用 enum Command
代替 enum ColorData
。enum Command
表示所有可能的命令(这里仅有 BoardLed
)。
+✅ RawCommandData
存储了消息主题的最后一部分(例如 a-uuid/command/board_led
中的 board_led
)。可以用 try_from
将其转换为 Command
。
+
+#![allow(unused)]
+fn main() {
+// RGB LED 命令
+let raw = RawCommandData {
+ path: command,
+ data: message.data(),
+};
+
+}
+
+检查 host-client
:
+✅ 你需要将 color
替换成 command
。例如:
+
+#![allow(unused)]
+fn main() {
+let command = Command::BoardLed(color)
+}
+
+✅ 在 process_message()
函数中,你需要解析主题。
+
+#![allow(unused)]
+fn main() {
+match message.details() {
+ Complete => {
+ // 本练习中的消息都会是 `Complete` 类型的
+ // `Details` 枚举的其他变体用于更大的消息 payload
+ //
+
+ // Cow<str> 的行为很像其他 Rust 字符串 (&str, String)
+ let topic: Cow<str> = message.topic(token);
+
+ // 确认我们是否对这个主题感兴趣
+ // 并根据它的内容来分发
+ let is_command_topic: bool = /* ... */;
+ if is_command_topic {
+ let raw = RawCommandData { /* ... */ };
+ if let Ok(Command::BoardLed(color)) = Command::try_from(raw) {
+ // 把 LED 配置为新收到的颜色
+ }
+
+ },
+ _ => {}
+ }
+}
+}
+
+💡 由于你需要遍历 MQTT 主题,你需要对字符串执行 split()
并得到一个迭代器。你可以用 nth()
来直接访问迭代器中的特定项。
+💡 可以用 cargo run --example solution2
运行实现了层次结构的解答。可以用 cargo run
或 cargo run --example solution1
运行未实现层次结构的解答。
+
+✅ 利用 serde_json
将消息数据编码/解码为 JSON。
+✅ 从主机客户端上发送一些带有大量 payload 的消息,并在微控制器上处理它们。大体积的消息将会分部分传递,而不是使用 Details::Complete
:
+
+#![allow(unused)]
+fn main() {
+InitialChunk(chunk_info) => { /* 第一块 */},
+SubsequentChunk(chunk_data) => { /* 所有后续块 */ }
+}
+
+💡 不需要根据消息 ID 来区分收到的块,因为在任意时刻,最多只有一条消息正在传输。
+
+
+
+- 构建示例客户端时出现 error: expected expression, found .:将你的 stable Rust 更新到 1.58 或更新的版本
+- 没有显示 MQTT 消息?确保所有客户端(板子和电脑)使用的是相同的 UUID(你可以在日志输出中看见它)
+
+
+