Как отправлять и получать пакеты UDP на Lego Mindstorms EV3 с использованием внешнего режима Simulink?

Я пытаюсь создать блок simulink, используя c-код для моего EV3, чтобы отправлять измеренное значение с его датчиков и/или получать данные с другого оборудования (raspberry pi) через пакеты UDP. Однако я не могу найти конкретного примера в Интернете. Я попытался написать свой собственный код по примеру с https://www.abc.se/~m6695/udp.html. Я ожидал, что это сработает, так как EV3 — это Linux-система. Однако это не работает.

Библиотека c-кода udp_receiver (обновлена):

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

// BUFLEN = max size
#define BUFLEN 512
#define NPACK 1


double * linux_udp_empfaenger(int32_t PORT)
{
  struct sockaddr_in si_me, si_other;

  int s, i, j, slen = sizeof(si_other) , recv_len;
  char buf[BUFLEN];
  //char buf_h[BUFLEN];
  unsigned short recv_port = (unsigned short) PORT;
  double * data;

  //create a UDP socket
  if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
  {
    return 0;
    exit(1);
  } else{

    // zero out the structure
    memset((char *) &si_me, 0, sizeof(si_me));

   si_me.sin_family = AF_INET;
   si_me.sin_port = htons(recv_port);
   si_me.sin_addr.s_addr = htonl(INADDR_ANY);

   //bind socket to port
   if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
   {
     close(s);
     return 0;
     exit(1);
   } else{

    //clear the buffer by filling null, it might have previously received data
    //memset(buf,'\0', BUFLEN);

    for (i=0; i<NPACK; i++) {
     if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)==-1)
      {
        close(s);
        return 0;
       } else {
        //Convert buf into usable host format
        //buf_h = ntohs(buf);

        // Convert data from string to double
        //data = atof(buf);

        int N_DATA = sizeof(buf)/sizeof(buf[0]);

        for (j = 0; j < N_DATA; j++){
        data[j] = (double)be64toh(((uint64_t *)buf)[j]);
        }

        close(s);
        return data;
      } 

    }

  }

 }

}

Когда я запускаю блок извне, модель отображается работающей, но T = 0,000 все время. Я даже не могу остановить модель сейчас.

Надеюсь получить от вас помощь. Спасибо, парни!


person limck    schedule 18.11.2015    source источник
comment
Вы используете стандартную прошивку от LEGO Group на EV3 или используете стороннюю ОС? Кроме того, оба ваших примера кода выглядят так, как будто они получают данные, а что они передают? Вы компилируете и устанавливаете код c на EV3?   -  person David Lechner    schedule 18.11.2015
comment
Здравствуйте, Дэвид, я использую стандартную прошивку от LEGO Group. И да, я пишу код для получения данных. По сути, я хотел бы получать все виды данных, используя этот код, особенно измеренные данные с другого оборудования. На данный момент я отправляю бесконечный синусоидальный сигнал с моего Raspberry Pi и желаю, чтобы EV3 мог получить сигнал. C-код компилируется вместе с моделью Simulink на EV3 и выполняется там функциональным блоком Matlab.   -  person limck    schedule 18.11.2015
comment
Я только что загрузил предупреждение, которое получил во время компиляции в вопросе выше. Надеюсь, вы мне поможете, спасибо :)   -  person limck    schedule 18.11.2015


Ответы (1)


./udp_send_receive.c:14: предупреждение: несовместимое неявное объявление встроенной функции «выход»

Вам не хватает

#include <stdlib.h>

./udp_send_receive.c:31: предупреждение: несовместимое неявное объявление встроенной функции 'memset'

./udp_send_receive.c:66: предупреждение: несовместимое неявное объявление встроенной функции 'memset'

./udp_send_receive.c:76: предупреждение: несовместимое неявное объявление встроенной функции 'strlen'

Вам не хватает

#include <string.h>

./udp_send_receive.c:44: предупреждение: передача аргумента 5 'recvfrom' из несовместимого типа указателя

Не совсем уверен, как исправить это. Определение __USE_GNU может помочь. См. sys/socket.h.

./udp_send_receive.c:70: предупреждение: передача аргумента 1 из 'inet_aton' делает указатель из целого числа без приведения

./udp_send_receive.c:76: предупреждение: передача аргумента 1 из 'strlen' делает указатель из целого числа без приведения

Необходимо добавить * в объявления параметров. char — одиночный символ. char * — это строка.

void udp_send(char *data, char *SERVER, int PORT)
person David Lechner    schedule 18.11.2015
comment
Спасибо, Дэвид! Исправил все ошибки и предупреждения в c-коде. Исправленные коды обновляются в вопросе. Тем не менее, модель все еще работает с T = 0,0000 и зависает там. Мне интересно, существуют ли те заголовки, которые я включил, в EV3. Есть ли способ проверить это? - person limck; 19.11.2015
comment
Вы никогда не выделяете память для buf. Я представляю, что программа получает segfult, когда вы пытаетесь использовать buf. В своем исходном сообщении вы использовали buf[BUFLEN];, который выделяет память в стеке и, скорее всего, это то, что вы хотите сделать здесь. - person David Lechner; 19.11.2015
comment
Спасибо, Дэвид. Я исправил это и попытался понемногу запускать c-код. И когда я комментирую функцию recvfrom(), как то, что я только что обновил в своем вопросе, модель работает гладко, но, конечно, данные не получены, и данные все время возвращаются -1. Итак, во-первых, я подумал, что он должен возвращать 0 в качестве значения, если данные не получены, так как я объявил double data = 0;? Также я не могу понять, почему функция recvfrom не работает. - person limck; 19.11.2015
comment
У меня нет доступа к simulink, поэтому я не знаю, как там на самом деле возвращаются данные и почему это будет -1. Функция recvfrom() блокируется, пока не получит данные. Вы действительно отправляете ему данные? Кроме того, в настоящее время у вас есть return data до close(s), поэтому сокет не закрывается должным образом. - person David Lechner; 19.11.2015
comment
Я понимаю. Я еще не отправлял на него никаких данных. Но что я хочу сделать, так это то, что если данные не будут получены в течение периода ожидания, данные вернут 0. Есть ли способ добиться этого? Кроме того, когда возникают ошибки при создании или привязке сокета, данные также возвращают 0. (Но я не совсем уверен, правильно ли я написал. В основном я хочу выйти из функции и вернуть выходные данные как 0) . Функция вызывается в бесконечных циклах, пока я не остановлю модель. Будет здорово, если я получу данные как 0 вместо ожидания получения данных, чтобы другие компоненты в модели также могли работать. - person limck; 19.11.2015
comment
Если вы запускаете это в цикле, я бы сделал отдельную функцию для привязки сокета (вам нужно будет сделать сокет глобальной переменной). Затем просто выполните прием в цикле и получите еще одну отдельную функцию для закрытия сокета, когда вы закончите. Вы можете использовать fcntl, чтобы сделать recvfrom неблокирующим. См. справочную страницу recvfrom. - person David Lechner; 19.11.2015
comment
Большое спасибо, Давид! Я почти там! Однако есть ли способ, которым я могу напрямую отправлять/получать тип double вместо преобразования double в char*, чтобы отправить его, а затем снова преобразовать из char* в double после его получения? - person limck; 20.11.2015
comment
Да и нет. Тип данных по-прежнему будет char *, но вместо преобразования его в строку вы можете отправить его как двоичные данные. double — это 64-битное число с плавающей запятой. Так, например, в функции отправки у вас может быть buf[sizeof(double)];, а позже вместо sprintf у вас будут *(uint64_t *)buf = htobe64((uint64_t)data); и size_t n = sizeof(double);. Функция приема будет аналогичной. data = (double)be64toh(*(uint64_t *)buf); - person David Lechner; 20.11.2015
comment
Извините, я не ясно сформулировал вопрос. То, что я хотел бы отправить и получить, должно быть вектором или матрицей двойных значений, а не только одним значением каждый раз. Есть ли какой-нибудь трюк, чтобы справиться с этим? - person limck; 20.11.2015
comment
Конечно, вы можете использовать массив с плавающей запятой таким же образом. Таким образом, код в моем предыдущем комментарии изменится на buf[sizeof(double) * N_DATA];, ((uint64_t *)buf)[i] = htobe64((uint64_t)data[i]);, size_t n = sizeof(double) * N_DATA;, data[i] = (double)be64toh(((uint64_t *)buf)[i]);, где N_DATA определяется как количество значений, которые вы отправляете, а i находится в цикле for (i = 0; i < N_DATA; i++). - person David Lechner; 20.11.2015
comment
Это символ buf[sizeof(double) * N_DATA];? sendto(s, buf, n , 0 , (struct sockaddr *) &si_other, slen) возвращает ошибку, когда я пытаюсь отправить данные. Модифицированный код снова обновляется в вопросе. - person limck; 20.11.2015
comment
Я думал, что N_DATA будет константой, но на самом деле было бы лучше передать его как параметр вместе с data, и в этом случае я бы сделал его строчным. - person David Lechner; 21.11.2015
comment
Спасибо, Дэвид. Отправляющая часть работает нормально после того, как я изменил N_DATA на n-data. Однако recvfrom() возвращает ошибку, когда я отправляю ему массив. Получатель не сможет узнать размер принимающего массива, поэтому я устанавливаю для него константу в качестве максимального размера. Почему возвращает ошибку? - person limck; 21.11.2015
comment
Я узнал, где ошибка. Это связано не с recvfrom(), а с int n_data = sizeof(buf)/sizeof(buf[0]);, где n_data будет использоваться в цикле как for (j = 0; i < N_DATA; j++), и в результате data[j] = (double)be64toh(((uint64_t *)buf)[j]); не входит в индекс. Но как узнать количество получаемых данных? - person limck; 21.11.2015
comment
Вы можете использовать константу и каждый раз отправлять одно и то же количество данных (некоторые значения будут равны 0, если нет фактического значения). Или вы можете отправить размер в качестве первого байта данных, затем прочитать один байт, чтобы получить размер, а затем прочитать остальные данные. - person David Lechner; 21.11.2015
comment
Хорошо, спасибо. Теперь я понял. Есть одна очень странная вещь об отправителе. Он отправлял правильные данные на правильный адрес ранее, когда аргумент был двойными данными (конечно, можно было отправлять только скалярные данные). Однако после того, как я изменил его на двойные * данные (с n_data и циклом) и попытался отправить скалярные данные, он возвращает ошибку в inet_aton(), из-за которой ему не удается изменить ip_add на структуру адреса. И если я отправляю векторные данные, ошибка не возвращается, но это неправильный адрес. Как это могло случиться? Изменение типа аргумента данных не должно иметь ничего общего с IP-адресом. - person limck; 21.11.2015
comment
Вы можете случайно написать за конец массива и записать структуры данных IP-адреса. - person David Lechner; 21.11.2015
comment
Да, ты прав. Структура вектора Matlab и структура массива в c различны. Теперь это решено. Однако как я могу передать скалярный параметр c-функции, которая ожидает аргумент массива? Я попытался передать скалярные данные c-функции, которая ожидает double * data, и данные не отправляются на назначенный IP-адрес (хотя ошибка не возвращается). Конечно, я могу создать 2 похожие c-функции, одну для скалярной и одну для векторной, но это бесполезно и выглядит неаккуратно. - person limck; 22.11.2015
comment
Вы можете просто использовать массив длиной один для скалярного значения или можете передать по ссылке (&data), что, по сути, одно и то же. - person David Lechner; 22.11.2015
comment
Спасибо, Дэвид. Я успешно создал блоки с вашей помощью. Однако я понял, что скорость передачи уменьшается со временем. Чем дольше я запускаю модель, тем медленнее она может передавать данные. Это нормально? Есть ли какие-либо предложения по улучшению этого? - person limck; 23.11.2015
comment
Извините, не уверен, что может быть причиной замедления. - person David Lechner; 23.11.2015