Вопрос или проблема
Я пытаюсь подключить два жука 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 и внимательно относитесь к обработке сообщений. Обновленные и исправленные части вашего кода помогут эффективно управлять асинхронными вызовами и обеспечат надежное соединение в вашей сети.