КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Соединяем две Piranha ESP32 по UDP и передаём массив данных

Общие сведения:

В этом проекте мы соединим две Piranha ESP32 по WiFi и будем передавать массивы данных с одной на другую при помощи протокола UDP.

UDP (англ. User Datagram Protocol) — протокол пользовательских датаграмм. UDP предоставляет ненадёжный сервис, и датаграммы могут прийти не по порядку, дублироваться или вовсе исчезнуть без следа. UDP подразумевает, что проверка ошибок и исправление либо не нужны, либо должны исполняться в приложении.

Фомат массива

При передаче и получении информации используются байты. Одним байтом возможно передать число от 0 до 255, что соответствует типу данных byte и uint8_t, а так же от -128 до 127, что соответствует типам char и int8_t. При передаче б_о_льших чисел используется трюк с преобразованием типов адресов (указателей). Например, если это массив с типом данных int (что соответствует четырём байтам для esp32), то каждый элемент массива отсылается побайтово. Таким образом число типа int i = -1 будет передано как четыре байта со значениями 0xff. Далее принимающая сторона преобразует байты обратно в int. Этот приём работает только при приёме/передаче данных представляющих непрерывные структуры в памяти, таких как массивы. Со связанными списками это приём не работает. При таком подходе очень легко выйти за границы массива, поэтому при передаче данных передаётся и их размер в байтах полученный при помощи оператора sizeof. Принимающая сторона вычисляет количество элементов приводя количество полученных байтов к размеру типа ожидаемых данных, так же при помощи оператора sizeof.

При передаче и приёме данных используйте одинаковые типы данных! В примерах используется проверка размера данных, но всё равно что-то может пойти не так. Если принимающая сторона будет ожидать int, а передающая сторона передаст char, то может произойти чтение или запись в ненадлежащую область памяти, от чего ESP32 может зависнуть, перезагружаться или работать непредсказуемо.

Видео:

редактируется ...

Нам понадобится:

Подключение:

Скетчи проекта для прямого подключения (esp32 - esp32):

Скетч для ESP32 в качестве UDP сервера

В данном скетче отправка данных осуществляется при помощи функции printf, а получение при помощи преобразования указателей. Это сделано для демонстрации двух способов. В первом случае получатель должен парсить строку, во втором отправитель должен отсылать только байты необходимых данных.

// Подключаем библиотеки
#include <AsyncUDP.h>
#include <WiFi.h>
#include <WiFiAP.h>

// Определяем название и пароль точки доступа
const char* ssid = "esp32asAP";
const char* password = "password12345";

// Создаём объект UDP соединения
AsyncUDP udp;

// Определяем порт
const uint16_t PORT = 49152;

// Массив данных для отправки
int SDATA[2] {1984, 2021};

// Определяем callback функцию обработки пакета
void parsePacket(AsyncUDPPacket packet)
{

    // Записываем адрес начала данных в памяти
    int* pdata = (int*)packet.data();

    // Вычисляем размер данных
    const size_t len = packet.length() / sizeof(&pdata);

    // Если адрес данных не равен нулю и размер данных больше нуля...
    if (pdata != NULL && len > 0) {

        // Проходим по элементам массива
        for (size_t i = 0; i < len; i++) {

            // Выводим каждый элемент в последовательный порт
            Serial.print(pdata[i]);
            Serial.print(", ");
        }

        Serial.println();

        // Отправляем данные клиенту
        packet.printf("Пакет получен. Ответ: %d, %d\n", SDATA[0], SDATA[1]);
    }
}


void setup()
{

    // Инициируем последовательный порт
    Serial.begin(115200);
    Serial.println();
    Serial.println("Инициируем точку доступа WiFi");

    // Инициируем точку доступа WiFi
    WiFi.softAP(ssid, password);
    IPAddress myIP = WiFi.softAPIP();

    // Выводим IP-адрес
    Serial.print("IP-адрес точки доступа: ");
    Serial.println(myIP);

    // Инициируем сервер
    if(udp.listen(PORT)) {

        // При получении пакета вызываем callback функцию
        udp.onPacket(parsePacket);
    }

    Serial.println("Сервер запущен.");
}

void loop()
{
    delay(10);
}

Скетч для ESP32 в качестве UDP клиента

В скетче клиента отправка данных осуществляется побайтово, а приём при помощи парсинга строки.

// Подключаем библиотеки
#include "WiFi.h"
#include "AsyncUDP.h"

// Создаём переменную состояния кнопки
bool keyState = false;

// Массив данных для отправки
int DATA[2] {11276, 1969};

// Определяем название и пароль точки доступа
const char *ssid = "esp32asAP";
const char *password = "password12345";

// Определяем порт
const uint16_t PORT = 49152;

// Создаём объект с IP-адресом точки доступа
IPAddress ADDR(192, 168, 4, 1);

// Создаём объект UDP соединения
AsyncUDP udp;

// Определяем callback функцию обработки пакета
void parsePacket(AsyncUDPPacket packet)
{
    String msg = packet.readStringUntil(':');
    int first = packet.readStringUntil(',').toInt();
    int second = packet.readStringUntil('\n').toInt();

    // Выводим преобразованные данные в последовательный порт
    Serial.printf("%s %d, %d\n", msg.c_str(), first, second);
}

void setup()
{
    // Инициируем последовательный порт
    Serial.begin(115200);

    // Устанавливаем режим работы в качестве клиента
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);

    // Ждём подключения WiFi
    while (WiFi.waitForConnectResult() != WL_CONNECTED) {
        Serial.print(".");
        delay(100);
    }

    // Если удалось подключиться по UDP
    if (udp.connect(ADDR, PORT)) {

        Serial.println("UDP подключён");

        // вызываем callback функцию при получении пакета
        udp.onPacket(parsePacket);
    }

    // Если подключение не удалось
    else {

        Serial.println("UDP не подключён");

        // Входим в бесконечный цикл
        while(1) {
            delay(1000);
        }
    }
}

void loop()
{
    // Отправляем данные серверу побайтово
    udp.broadcastTo((uint8_t*)&DATA, sizeof(DATA), PORT);

    // Если соединение WiFi прервано
    if (WiFi.status() != WL_CONNECTED) {

        // Вызываем функцию setup(), для повторного подключения
        setup();
    }

    delay(1000);
}

Скетчи проекта для подключения через WiFi роутер (esp32 - роутер - esp32):

При таком подключении необходимо сначала загрузить скетч с сервером UDP. В последовательный порт будет выведен IP-адрес сервера, который необходимо указать в скетче клиента. Так же в обоих сетчах необходимо указать название точки доступа и пароль. Обе ESP32 должны находиться в одной сети. При таком подключении передача данных немного медленее, чем примое подключение (оверхед роутера на маршрутизацию пакетв).

Скетч для ESP32 в качестве UDP сервера

// Подключаем библиотеки
#include <AsyncUDP.h>
#include <WiFi.h>

// Определяем название и пароль точки доступа
const char* ssid = "название WiFi";
const char* password = "пароль WiFi";

// Создаём объект UDP соединения
AsyncUDP udp;

// Определяем порт
const uint16_t PORT = 49152;

// Массив данных для отправки
int SDATA[2] {1984, 2021};

// Массив для получения
int rdata[2] {0};

// Кол-во элементов массива для получения
const size_t glen = sizeof(rdata) / sizeof(rdata[0]);

// Определяем callback функцию обработки пакета
void parsePacket(AsyncUDPPacket packet)
{

    // Записываем адрес начала данных в памяти
    const int* tmp = (int*)packet.data();

    // Вычисляем размер данных
    const size_t len = packet.length() / sizeof(&rdata);

    // Если адрес данных не равен нулю и размер данных больше нуля...
    if (tmp != nullptr && len > 0) {

        // Проходим по элементам массива
        for (size_t i = 0; i < len && i < glen; i++) {

            // Записываем в глобальную переменную
            rdata[i] = tmp[i];
        }

        // Отправляем данные клиенту
        packet.printf("Пакет получен. Ответ: %d, %d\n", SDATA[0], SDATA[1]);
    }
}


void setup()
{

    // Инициируем последовательный порт
    Serial.begin(115200);

    // Устанавливаем режим работы в качестве клиента
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);

    // Ждём подключения WiFi
    while (WiFi.waitForConnectResult() != WL_CONNECTED) {
        Serial.print(".");
        delay(100);
    }

    // Выводим IP-адрес
    IPAddress myIP = WiFi.localIP();
    Serial.print("IP-адрес точки доступа: ");
    Serial.println(myIP);

    // Инициируем сервер
    if(udp.listen(PORT)) {

        // При получении пакета вызываем callback функцию
        udp.onPacket(parsePacket);
    }

    Serial.println("Сервер запущен.");
}

void loop()
{

    // Выводим значение элементов массива
    for (size_t i = 0; i < glen; i++) {
        Serial.println(rdata[i]);
    }

    delay(1000);
}

Скетч для ESP32 в качестве UDP клиента

// Подключаем библиотеки
#include "WiFi.h"
#include "AsyncUDP.h"

// Создаём переменную состояния кнопки
bool keyState = false;

// Массив данных для отправки
int DATA[2] {11276, 1969};

// Определяем название и пароль точки доступа
const char* ssid = "название WiFi";
const char* password = "пароль WiFi";

// Определяем порт
const uint16_t PORT = 49152;

// Определяем адрес сервера (например "192.168.1.3")
const char* SER_IP = "n.n.n.n";

// Создаём объект UDP соединения
AsyncUDP udp;

// Переменные для получения данных
String msg = "";
int first = 0;
int second = 0;

// Определяем callback функцию обработки пакета
void parsePacket(AsyncUDPPacket packet)
{
    msg = packet.readStringUntil(':');
    first = packet.readStringUntil(',').toInt();
    second = packet.readStringUntil('\n').toInt();
}

void setup()
{
    // Инициируем последовательный порт
    Serial.begin(115200);

    // Устанавливаем режим работы в качестве клиента
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);

    // Ждём подключения WiFi
    while (WiFi.waitForConnectResult() != WL_CONNECTED) {
        Serial.print(".");
        delay(100);
    }

    // Инициируем объект IP-адреса
    IPAddress addr;
    // Вычисляем IP-адрес из строки
    addr.fromString(SER_IP);
    // Если удалось подключиться по UDP
    if (udp.connect(addr, PORT)) {

        Serial.println("UDP подключён");

        // вызываем callback функцию при получении пакета
        udp.onPacket(parsePacket);
    }

    // Если подключение не удалось
    else {

        Serial.println("UDP не подключён");

        // Входим в бесконечный цикл
        while(1) {
            delay(1000);
        }
    }
}

void loop()
{
    // Отправляем данные серверу побайтово
    udp.broadcastTo((uint8_t*)&DATA, sizeof(DATA), PORT);

    // Выводим полученные данные в последовательный порт
    Serial.printf("%s %d, %d\n", msg.c_str(), first, second);

    // Если соединение WiFi прервано
    if (WiFi.status() != WL_CONNECTED) {

        // Вызываем функцию setup(), для повторного подключения
        setup();
    }

    delay(1000);
}

Ссылки




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями