Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Упражнение MQTT: получение команд для светодиода

✅ Подпишитесь на color_topic(uuid).

✅ Запустите host_client параллельно, в отдельном терминале. host_client публикует color светодиода платы примерно раз в секунду.

✅ Проверьте, что подписка работает, выведя в лог информацию, полученную через топик.

✅ Реагируйте на команды светодиода: установите полученный цвет на плате с помощью led.set_pixel(/* received color here */).

intro/mqtt/exercise/solution/solution_publ_rcv.rs содержит решение. Запустить его можно командой:

cargo run --example solution_publ_rcv

Кодирование и декодирование полезной нагрузки сообщений

Команды светодиода платы состоят из трёх байтов, которые обозначают красный, зелёный и синий.

  • enum ColorData содержит топик color_topic(uuid) и BoardLed;
  • поле data() структуры EspMqttMessage можно преобразовать с помощью try_from(). Сначала сообщение нужно привести к срезу: let message_data: &[u8] = &message.data();
#![allow(unused)]
fn main() {
// команда RGB-светодиода

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 в замыкании-обработчике имеет тип EspMqttEvent, у которого есть метод payload() для доступа к EventPayload. Поскольку нас интересуют только успешно полученные сообщения:

#![allow(unused)]
fn main() {
    let mut client =
        EspMqttClient::new_cb(
            &broker_url,
            &mqtt_config,
            move |message_event| match message_event.payload() {
                Received { data, details, .. } => process_message(data, details, &mut led),
                Error(e) => warn!("Received error from MQTT: {:?}", e),
                _ => info!("Received from MQTT: {:?}", message_event.payload()),
            },
        )?;
}

В обработчике нужно обрабатывать сообщения Complete.

💡 Используйте Rust Analyzer, чтобы сгенерировать недостающие ветки match, или обработайте любой другой тип ответа, выведя его через info!().

#![allow(unused)]
fn main() {
fn process_message(data: &[u8], details: Details, led: &mut WS2812RMT) {
    match details {
        Complete => {
            info!("{:?}", data);
            let message_data: &[u8] = data;
            if let Ok(ColorData::BoardLed(color)) = ColorData::try_from(message_data) {
                info!("{}", color);
                if let Err(e) = led.set_pixel(color) {
                    error!("Could not set board LED: {:?}", e)
                };
            }
        }
        _ => {}
    }
}
}

💡 Используйте логгер, чтобы увидеть, что вы получаете, например info!("{}", color); или dbg!(color).

Дополнительные задания

Реализуйте MQTT с иерархическими топиками

✅ Выполняйте это, если всё остальное уже готово. Полного решения мы не даём: задача в том, чтобы проверить, как далеко вы продвинетесь самостоятельно.

Посмотрите common/lib/mqtt-messages:

✅ Реализуйте ту же процедуру, но с использованием иерархии MQTT. Подписывайтесь на все сообщения «command», комбинируя cmd_topic_fragment(uuid) с завершающим шаблоном #.

✅ Используйте enum Command вместо enum ColorData. enum Command представляет все возможные команды (здесь — только BoardLed).

✅ RawCommandData хранит последнюю часть топика сообщения (например, board_led в a-uuid/command/board_led). Его можно преобразовать в Command с помощью try_from.

#![allow(unused)]
fn main() {
// команда RGB-светодиода
let raw = RawCommandData {
    path: command,
    data: message.data(),
};
}

Посмотрите host-client:

✅ Нужно заменить color на command. Например, так:

#![allow(unused)]
fn main() {
let command = Command::BoardLed(color)
}

Другие задания

✅ Используйте serde_json, чтобы кодировать и декодировать данные сообщений в JSON.

✅ Отправьте с клиента-хоста несколько сообщений с большой полезной нагрузкой и обработайте их на микроконтроллере. Большие сообщения доставляются по частям вместо Details::Complete:

#![allow(unused)]
fn main() {
InitialChunk(chunk_info) => { /* первый фрагмент */},
SubsequentChunk(chunk_data) => { /* все последующие фрагменты */ }
}

💡 Не нужно различать входящие фрагменты по ID сообщения: в любой момент времени в пути может находиться не более одного сообщения.

Устранение неполадок

  • error: expected expression, found . при сборке host-клиента: обновите stable Rust до версии 1.58 или новее.
  • Не видно сообщений MQTT? Убедитесь, что все клиенты (плата и компьютер разработчика) используют один и тот же UUID (его видно в выводе логов).