esp-open-rtosを使ってみる

UDP Broadcast Send & Receive(Socket-API)

UDPのAPIの使い方が分かったので、UDPでメッセージをBroadcastするクライアントを作ってみました。
最初に以下のUDP受信処理(Pythonスクリプト)を別の端末で実行しておきます。
ポート番号=9876からBroadcastメッセージを受信するコードですが、Pythonだとたったこれだけです。
recv()からの戻り値の型がpython2とpython3では違います。
import select, socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('<broadcast>', 9876))
s.setblocking(0)

while True:
    result = select.select([s],[],[])
    msg = result[0][0].recv(1024)
    if (type(msg) is bytes):
        msg=msg.decode('utf-8')
    print(msg)

以下のコードでは、2つのタスクと1つのタイマーを起動しています。

task1(WiFiタスク)
APへの接続を監視するタスクです。
APへの接続が完了したらタイマーを起動します。

task2(Bradcast Sendタスク)
Queueから取り出したメッセージをUDPでBroadcastします。
ほぼ、こ ちらのコードのままです。探せばどこかにSocket-APIのコードは公開されています。

timer_cb(タイマー)
定期的にQueueにメッセージを積みます。

/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include <string.h>
#include <espressif/esp_common.h>
#include <esp/uart.h>
#include <FreeRTOS.h>
#include <task.h>
#include <timers.h>
#include <ssid_config.h>

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

#define UDP_PORT 9876

QueueHandle_t xQueue;
TimerHandle_t timerHandle;

UBaseType_t uxQueueLength = 10;
UBaseType_t uxItemSize = 64;

static void showNetworkInfo() {
  uint8_t hwaddr[6];
  if (sdk_wifi_get_macaddr(STATION_IF, hwaddr)) {
    printf("[%s] MAC address="MACSTR"\n",pcTaskGetName(0),MAC2STR(hwaddr));
  }
  struct ip_info info;
  if (sdk_wifi_get_ip_info(STATION_IF, &info)) {
    printf("[%s] IP address="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.ip));
    printf("[%s] Netmask="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.netmask));
    printf("[%s] Gateway="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.gw));
  }
}

// Timer call back
static void timer_cb(TimerHandle_t xTimer)
{
  char buffer[uxItemSize];
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
  sprintf(buffer,"Hello World!! %d",nowTick);
  xQueueSend(xQueue, &buffer, 0);
}

// WiFi Task
void task1(void *pvParameters)
{
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);

  /* Wait until we have joined AP and are assigned an IP */
  int wifiStatus;
  while (1) {
    wifiStatus = sdk_wifi_station_get_connect_status();
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] wifiStatus=%d\n",pcTaskGetName(0),nowTick,wifiStatus);
    //if (sdk_wifi_station_get_connect_status() == STATION_GOT_IP) break;
    if (wifiStatus == STATION_GOT_IP) break;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
  }
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Connected to AP\n",pcTaskGetName(0),nowTick);
  showNetworkInfo();

  /* Now start timer */
  if (xTimerStart(timerHandle, 0) != pdPASS) {
    printf("[%s:%d] Unable to start Timer\n",pcTaskGetName(0),nowTick);
  } else {
    printf("[%s:%d] Success to start Timer\n",pcTaskGetName(0),nowTick);
  }

  while(1) {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
  }
}


// Bradcast Send Task
void task2(void *pvParameters)
{
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);

  /* set up address to sendto */
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(UDP_PORT);
  addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); /* send message to 255.255.255.255 */

  /* create the socket */
  int fd;
  int ret;
  fd = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP ); // Create a UDP socket.
  LWIP_ASSERT("fd >= 0", fd >= 0);

  char buffer[uxItemSize];
  int buflen;
  while(1) {
    if(xQueueReceive(xQueue, &buffer, portMAX_DELAY)) {
       nowTick = xTaskGetTickCount();
       printf("[%s:%d] buffer=[%s]\n",pcTaskGetName(0),nowTick,buffer);
       buflen = strlen(buffer);
       ret = lwip_sendto(fd, buffer, buflen, 0, (struct sockaddr *)&addr, sizeof(addr));
       LWIP_ASSERT("ret == buflen", ret == buflen);
       nowTick = xTaskGetTickCount();
       printf("[%s:%d] lwip_sendto ret=%d\n",pcTaskGetName(0),nowTick,ret);
    } else {
       nowTick = xTaskGetTickCount();
       printf("[%s:%d] xQueueReceive fail\n",pcTaskGetName(0),nowTick);
       break;
    }
  }

  /* close socket. Don't reach here.*/
  ret = lwip_close(fd);
  LWIP_ASSERT("ret == 0", ret == 0);
  vTaskDelete( NULL );

}


void user_init(void)
{
  uart_set_baud(0, 115200);
  printf("SDK version:%s\n", sdk_system_get_sdk_version());
  printf("pcTaskGetName=%s\n",pcTaskGetName(0));

  struct sdk_station_config st_config = {
    .ssid =WIFI_SSID,
    .password =WIFI_PASS,
  };
  sdk_wifi_set_opmode(STATION_MODE);
  sdk_wifi_station_set_config(&st_config);

  /* Create Queue */
  xQueue = xQueueCreate(uxQueueLength, uxItemSize);
  /* Create Timer */
  timerHandle = xTimerCreate("Trigger", 5000/portTICK_PERIOD_MS, pdTRUE, NULL, timer_cb);

  /* Check everything was created. */
  configASSERT( xQueue );
  configASSERT( timerHandle );

  // Create Task
  xTaskCreate(task1, "WiFi", 256, NULL, 2, NULL);
  xTaskCreate(task2, "Client", 1024, NULL, 2, NULL);

}

Python側には以下の様に表示されます。


自動的に改行してくれませんが、UbuntuやArmbianのNetCat(nc)でも同じことができます。
RaspbianのNetCat(nc)ではkオプションが有効になりません。




Broadcastメッセージの受信処理をAPIで作る場合は、以下のコードとなります。
ほぼこ ちらのコードのままです。
/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include <string.h>
#include <espressif/esp_common.h>
#include <esp/uart.h>
#include <FreeRTOS.h>
#include <task.h>
#include <timers.h>
#include <ssid_config.h>

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

#define UDP_PORT 9876

SemaphoreHandle_t xSemaphore;

static void showNetworkInfo() {
  uint8_t hwaddr[6];
  if (sdk_wifi_get_macaddr(STATION_IF, hwaddr)) {
    printf("[%s] MAC address="MACSTR"\n",pcTaskGetName(0),MAC2STR(hwaddr));
  }
  struct ip_info info;
  if (sdk_wifi_get_ip_info(STATION_IF, &info)) {
    printf("[%s] IP address="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.ip));
    printf("[%s] Netmask="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.netmask));
    printf("[%s] Gateway="IPSTR"\n",pcTaskGetName(0),IP2STR(&info.gw));
  }
}

// WiFi Task
void task1(void *pvParameters)
{
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);

  /* Wait until we have joined AP and are assigned an IP */
  int wifiStatus;
  while (1) {
    wifiStatus = sdk_wifi_station_get_connect_status();
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] wifiStatus=%d\n",pcTaskGetName(0),nowTick,wifiStatus);
    //if (sdk_wifi_station_get_connect_status() == STATION_GOT_IP) break;
    if (wifiStatus == STATION_GOT_IP) break;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
  }
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Connected to AP\n",pcTaskGetName(0),nowTick);
  showNetworkInfo();

  /* Now start task2 */
  xSemaphoreGive(xSemaphore);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Give to task2\n",pcTaskGetName(0),nowTick);

  while(1) {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
  }
}


// Bradcast Receive Task
void task2(void *pvParameters)
{
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);

  /* wait for Semaphore */
  xSemaphoreTake(xSemaphore, portMAX_DELAY);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Take\n",pcTaskGetName(0),nowTick);

  /* set up address to recvfrom */
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(UDP_PORT);
  addr.sin_addr.s_addr = htonl(INADDR_ANY); /* senderInfo message from ANY */

  /* create the socket */
  int fd;
  int ret;
  fd = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP ); // Create a UDP socket.
  LWIP_ASSERT("fd >= 0", fd >= 0);

#if 0
  /* set option */
  int broadcast=1;
  ret = lwip_setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast);
  LWIP_ASSERT("ret >= 0", ret >= 0);
#endif

  /* bind socket */
  ret = lwip_bind(fd, (struct sockaddr *)&addr, sizeof(addr));
  LWIP_ASSERT("ret >= 0", ret >= 0);

  /* senderInfo data */
  char buffer[64];
  struct sockaddr_in senderInfo;
  socklen_t senderInfoLen = sizeof(senderInfo);
  char senderstr[16];
  while(1) {
    socklen_t senderInfoLen = sizeof(senderInfo);
    memset(buffer, 0, sizeof(buffer));
    ret = lwip_recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&senderInfo, &senderInfoLen);
    LWIP_ASSERT("ret > 0", ret > 0);
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] lwip_recv ret=%d\n",pcTaskGetName(0),nowTick,ret);
    if (ret > 0) {
      buffer[ret] = 0;
      printf("[%s:%d] lwip_recv buffer=%s\n",pcTaskGetName(0),nowTick,buffer);
      inet_ntop(AF_INET, &senderInfo.sin_addr, senderstr, sizeof(senderstr));
      printf("recvfrom : %s, port=%d\n", senderstr, ntohs(senderInfo.sin_port));
    }
  }

  /* close socket. Don't reach here. */
  ret = lwip_close(fd);
  LWIP_ASSERT("ret == 0", ret == 0);
  vTaskDelete( NULL );

}


void user_init(void)
{
  uart_set_baud(0, 115200);
  printf("SDK version:%s\n", sdk_system_get_sdk_version());
  printf("pcTaskGetName=%s\n",pcTaskGetName(0));

  struct sdk_station_config st_config = {
    .ssid =WIFI_SSID,
    .password =WIFI_PASS,
  };
  sdk_wifi_set_opmode(STATION_MODE);
  sdk_wifi_station_set_config(&st_config);


  /* Create Semaphore */
  xSemaphore = xSemaphoreCreateBinary();
  /* Check everything was created. */
  configASSERT( xSemaphore );

  // Create Task
  xTaskCreate(task1, "WiFi", 256, NULL, 2, NULL);
  xTaskCreate(task2, "Client", 1024, NULL, 2, NULL);

}

受信データだけでなく、送信側のIPアドレスや、送信側の待ち受けポート番号も表示しました。
このアドレスとポートに返信すると、相手にデータが届きます。


ポート番号=9876にBroadcastメッセージを送信するコードですが、Pythonだとやはりこれだけで済みます。
import sys
import socket
import time
import signal

def handler(signal, frame):
  global flag
  print('handler')
  flag = False


sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
signal.signal(signal.SIGINT, handler)
flag = True
while flag:
  #print("sys.version_info={}".format(sys.version_info))
  message='This is a test'
  if (sys.version_info >= (3, 0)):
    sock.sendto(message.encode('utf-8'), ('<broadcast>', 9876))
  else:
    sock.sendto(message, ('<broadcast>', 9876))
  time.sleep(10)

LinuxのncコマンドでBroadcastメッセージを送る場合、Broadcastメッセージを送ることができるncとできないncが有り ます。
socatを使えば間違いなくBroadcastメッセージを送ることができます。
$ echo -n 'Hello World!!' | nc -ub 255.255.255.255 9876

$ echo 'Hello World!!'  | socat - udp-datagram:255.255.255.255:9876,broadcast


続く....