Тред 1.3 на ESP32-C6: Как асинхронно реагировать на сообщения

Вопрос или проблема

Я пытаюсь подключить два жука esp32-c6 вместе в сети Thread 1.3. Они оба прошиты одним и тем же кодом. Если они обнаруживают сеть, то присоединяются к ней. Если сети нет, то они создают сеть и становятся её лидерами. Если они лидер, то отправляют широковещательное сообщение (“Привет, я ваш лидер.”). Если они узел, то отправляют сообщение только лидеру (“Привет от узла.”).

В коде есть следующая ошибка компиляции Ошибка компиляции: список выражений рассматривается как составное выражение в функциональном приведении [-fpermissive]. Я знаю, что у меня неправильно otUdpReceive(instance, onMessageReceived, &socket);, но не могу разобраться в документации, чтобы это исправить. Эта строка должна настроить асинхронную функцию, которая вызывает onMessageReceived при получении сообщения.

Вот более контекстный код (он в initThreadDevice)

if (otUdpBind(instance, &socket, &bindAddr, OT_NETIF_THREAD) == OT_ERROR_NONE) {
        Serial.println("UDP сокет привязан к порту.");

        // Установите обратный вызов получения UDP
        otUdpReceive(instance, onMessageReceived, &socket); // Зарегистрируйте обратный вызов
    } else {
        Serial.println("Не удалось привязать UDP сокет.");
    }
}

Вот полный код.

#include <Arduino.h>
#include <openthread/thread.h>
#include <openthread/udp.h>
#include <openthread/message.h>
#include <openthread/dataset.h>
#include <openthread/instance.h>
#include <openthread/tasklet.h>

#define THREAD_CHANNEL 15

otInstance *instance;
otUdpSocket socket;

// Функция обратного вызова для обработки полученных сообщений
void onMessageReceived(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) {

    // Получить Mesh-Local EID
    const otIp6Address *meshLocalEid = otThreadGetMeshLocalEid(instance);
    if (meshLocalEid == NULL) {
        Serial.println("Не удалось получить Mesh-Local EID.");
        return;
    }

    // Сравнить Mesh-Local EID с адресом источника входящего сообщения
    if (otIp6IsAddressEqual(meshLocalEid, &aMessageInfo->mPeerAddr)) {
        Serial.println("Игнорирование сообщения от самого себя.");
        return;  // Игнорировать сообщение, если оно от этого устройства
    }

    char messageBuffer[128];
    int maxLength = sizeof(messageBuffer) - 1;  // Установить максимальную длину сообщения (128 - 1 для завершения нуля)

    // Прочитать входящее сообщение, но обрезать его, если оно слишком длинное
    int length = otMessageRead(aMessage, otMessageGetOffset(aMessage), messageBuffer, maxLength);

    // Убедитесь, что сообщение завершается нулем
    if (length > 0) {
        messageBuffer[length] = '\0';  // Добавить завершение нуля в конец сообщения
    } else {
        return;  // Если длина недействительна, просто вернуться
    }

    // Печатаем полученное сообщение
    Serial.print("Получено сообщение: ");
    Serial.println(messageBuffer);
}

// Попробовать присоединиться к существующей сети Thread
bool joinExistingNetwork(otOperationalDataset &dataset) {
    if (dataset.mComponents.mIsActiveTimestampPresent) {
        if (otDatasetSetActive(instance, &dataset) == OT_ERROR_NONE) {
            Serial.println("Присоединен к существующей сети Thread.");
            return true;
        } else {
            Serial.println("Не удалось присоединиться к сети.");
        }
    } else {
        Serial.println("Активная сеть Thread не найдена.");
    }

    return false;
}

// Создать новую сеть Thread
bool formNewThreadNetwork(otOperationalDataset &dataset) {
    Serial.println("Создание новой сети Thread...");

    memset(&dataset, 0, sizeof(dataset));  // Очистить набор данных

    // Установите параметры сети (примерные значения, вы можете их настроить)
    dataset.mActiveTimestamp.mSeconds = 1;  // Установить временную метку в секундах
    dataset.mActiveTimestamp.mTicks = 0;    // Установить тики (обычно 0)
    dataset.mActiveTimestamp.mAuthoritative = true;  // Отметить как авторитетный
    dataset.mComponents.mIsActiveTimestampPresent = true;
    dataset.mComponents.mIsActiveTimestampPresent = true;

    dataset.mChannel = THREAD_CHANNEL;  // Установите нужный канал
    dataset.mComponents.mIsChannelPresent = true;

    dataset.mPanId = 0x1234;  // Установите случайный PAN ID
    dataset.mComponents.mIsPanIdPresent = true;

    const char* networkName = "MyThreadNetwork";
    strcpy(dataset.mNetworkName.m8, networkName);  // Установите имя сети
    dataset.mComponents.mIsNetworkNamePresent = true;

    // Примените новый набор данных для формирования сети
    if (otDatasetSetActive(instance, &dataset) == OT_ERROR_NONE) {
        Serial.println("Новая сеть Thread создана.");
        return true;
    } else {
        Serial.println("Не удалось создать новую сеть Thread.");
    }

    return false;
}

// Инициализация устройства Thread и настройка UDP связи
void initThreadDevice() {
    Serial.println("Инициализация Thread...");

    Serial.println("Обнаружение существующих сетей...");
    otOperationalDataset dataset;

    if (otDatasetGetActive(instance, &dataset) == OT_ERROR_NONE) {
      Serial.println("Активная сеть Thread найдена. Попытка присоединиться...");
      if (!joinExistingNetwork(dataset)) {
        Serial.println("Не удалось присоединиться к существующей сети. Преждевременный выход из initThreadDevice.");
        return;
      }
    } else {
      Serial.println("Активная сеть Thread не найдена. Попытка создать новую сеть...");
      if (!formNewThreadNetwork(dataset)) {
        Serial.println("Не удалось создать новую сеть. Преждевременный выход из initThreadDevice.");
        return;
      }
    }

    // Попробуйте запустить протокол Thread
    if (otThreadSetEnabled(instance, true) == OT_ERROR_NONE) {
        Serial.println("Сеть Thread включена.");
    } else {
        Serial.println("Не удалось запустить сеть Thread.");
        return;
    }

    // Настройте UDP сокет и привяжите его к порту
    otSockAddr bindAddr;
    memset(&bindAddr, 0, sizeof(bindAddr));
    bindAddr.mPort = 12345;  // Номер порта (пример)

if (otUdpBind(instance, &socket, &bindAddr, OT_NETIF_THREAD) == OT_ERROR_NONE) {
        Serial.println("UDP сокет привязан к порту.");

        // Установите обратный вызов получения UDP
        otUdpReceive(instance, onMessageReceived, &socket); // Зарегистрируйте обратный вызов
    } else {
        Serial.println("Не удалось привязать UDP сокет.");
    }
}

void broadcastMessage(const char *message) {
    delay(random(100, 500));
    otMessageInfo messageInfo;
    memset(&messageInfo, 0, sizeof(messageInfo));  // Очистить структуру messageInfo

    // Установите адрес назначения на многоадресный адрес IPv6 link-local ff02::1 (все узлы)
    if (otIp6AddressFromString("ff02::1", &messageInfo.mPeerAddr) != OT_ERROR_NONE) {
        Serial.println("Неверный многоадресный адрес.");
        return;
    }
    messageInfo.mPeerPort = 12345;  // Укажите порт для отправки сообщения

    // Создайте новое UDP сообщение
    otMessage *udpMessage = otUdpNewMessage(instance, NULL);
    if (udpMessage == NULL) {
        Serial.println("Не удалось создать новое UDP сообщение.");
        return;
    }

    // Добавьте сообщение в буфер UDP сообщения
    if (otMessageAppend(udpMessage, message, strlen(message)) != OT_ERROR_NONE) {
        Serial.println("Не удалось добавить содержимое сообщения.");
        otMessageFree(udpMessage);
        return;
    }

    // Отправьте сообщение по UDP на многоадресный адрес
    otUdpSend(instance, &socket, udpMessage, &messageInfo);

    Serial.println("Широковещательное сообщение отправлено.");
    otMessageFree(udpMessage);
}

void sendMessageToLeader(const char *message) {
    delay(random(100, 500));
    otMessageInfo messageInfo;
    memset(&messageInfo, 0, sizeof(messageInfo));  // Очистить структуру messageInfo

    // Получите RLOC (Routing Locator) лидера
    otIp6Address leaderAddress;
    if (otThreadGetLeaderRloc(instance, &leaderAddress) != OT_ERROR_NONE) {
        Serial.println("Не удалось получить адрес лидера.");
        return;
    }

    // Установите адрес назначения на адрес лидера
    messageInfo.mPeerAddr = leaderAddress;
    messageInfo.mPeerPort = 12345;  // Укажите порт для отправки сообщения

    // Создайте новое UDP сообщение
    otMessage *udpMessage = otUdpNewMessage(instance, NULL);
    if (udpMessage == NULL) {
        Serial.println("Не удалось создать новое UDP сообщение.");
        return;
    }

    // Добавьте сообщение в буфер UDP сообщения
if (otMessageAppend(udpMessage, message, strlen(message)) != OT_ERROR_NONE) {
    Serial.println("Не удалось добавить содержимое сообщения.");
    otMessageFree(udpMessage);
    return;
}
    // Отправьте сообщение по UDP на адрес лидера
    otUdpSend(instance, &socket, udpMessage, &messageInfo);

    Serial.println("Сообщение отправлено лидеру сети.");
    otMessageFree(udpMessage);
}

bool isLeader() {
    otDeviceRole role = otThreadGetDeviceRole(instance);  // Получите текущую роль устройства

    if (role == OT_DEVICE_ROLE_LEADER) {
      return true;
    } 

    return false;
}

void setup() {
    Serial.begin(115200);
    Serial.println("Запуск связи ESP32-C6 Thread...");

    // Инициализация экземпляра OpenThread
    instance = otInstanceInitSingle();

    // Инициализация устройства Thread (попытка присоединиться или создать сеть)
    initThreadDevice();
}

void loop() {
    // Отправляем тестовые сообщения периодически (для лидера или узла)
    if (isLeader()) {
        broadcastMessage("Привет от лидера.");
    } else {
        sendMessageToLeader("Привет от узла.");
    }

    // Задержка между обменами сообщениями
    delay(5000);

    // Процессы OpenThread должны вызываться периодически
    otTaskletsProcess(instance);
}

Ответ или решение

Асинхронный ответ на сообщения в сети Thread 1.3 на ESP32-C6

Ваша задача заключается в разработке сети Thread 1.3, используя микроконтроллер ESP32-C6, и необходимо правильно настроить асинхронное получение сообщений с правильной регистрацией обратного вызова. Вы столкнулись с ошибкой компиляции, которая указывает на проблему с неправильным использованием функции otUdpReceive. Рассмотрим более детально, как это исправить и оптимизировать ваш код.

Проблема компиляции

Ошибка компиляции, которую вы наблюдаете:

Compilation error: expression list treated as compound expression in functional cast [-fpermissive]

возникает из-за неверного синтаксиса при вызове функции otUdpReceive. Функция otUdpReceive использует три параметра, и важно правильно передать параметры в этой функции, обратный вызов, контекст и сокет.

Исправление кода

В строке:

otUdpReceive(instance, onMessageReceived, &socket);

необходимо убедиться, что используемый сокет и его контекст правильно передаются. Необходимо использовать otUdpSetReceiveCallback вместо otUdpReceive. Правильный вызов будет выглядеть следующим образом:

otUdpSetReceiveCallback(instance, &socket, onMessageReceived, NULL); // Регистрация обратного вызова

Полное описание изменений

Кроме исправления синтаксиса, полезно удостовериться, что ваши функции определения и обработки сообщений написаны корректно. Обратите внимание на следующие моменты:

  • Обработка сообщения: Вы правильно извлекаете сообщение из otMessage и проверяете его длину. Убедитесь, что вы корректно читаете данные:
    • Используйте otMessageRead с правильным смещением, чтобы избежать доступа к неинициализированной памяти.
  • Сетевые адреса: Поддержание правильных сетевых адресов и взаимодействие с leader-узлом должно происходить корректно, чтобы избежать путаницы в получении сообщений.

Итог

Ваша основная задача заключается в регистрации функции обратного вызова для асинхронной обработки сообщений. После внедрения исправлений ваш код должен стать более чистым и исправным. Обеспечьте правильное применение функций работы с сетевыми сообщениями и настройку сокетов. Так, исправленный участок кода будет выглядеть следующим образом:

if (otUdpBind(instance, &socket, &bindAddr, OT_NETIF_THREAD) == OT_ERROR_NONE) {
    Serial.println("UDP socket bound to port.");

    // Set the UDP receive callback using the correct function
    otUdpSetReceiveCallback(instance, &socket, onMessageReceived, NULL); // Регистрация обратного вызова
} else {
    Serial.println("Failed to bind UDP socket.");
}

Это изменение должно решить вашу проблему с компиляцией и обеспечит корректное функционирование вашего кода в сети Thread. Убедитесь также, что у вас настроена соответствующая среда для разработки и все необходимые библиотеки OpenThread корректно подключены.

Заключение

Ваша реализация сетевого взаимодействия между ESP32-C6 через Thread 1.3 может быть успешной, если вы следуете рекомендациям, учитываете нюансы работы с функциями OpenThread и внимательно относитесь к обработке сообщений. Обновленные и исправленные части вашего кода помогут эффективно управлять асинхронными вызовами и обеспечат надежное соединение в вашей сети.

Оцените материал
Добавить комментарий

Капча загружается...