esp-open-rtosを使ってみる

TCP Client(Socket-API)

ESP8266のFreeRTOSにはlwIP(A Lightweight TCP/IP stack)が最初から含まれています。
lwIPには3つのAPIが用意されています。
・raw API(callback API)
・Sequential-style API(Netconn APIとNETIF API)
・Socket API(BSD-Socket API)
これらの違いについてはこちらに説 明が有ります。

lwIPのSocket APIの一覧がこちらで 公開されていますが、LinuxのSocketと同じように使えるので楽ちんです。
関数名の先頭にlwip_が付くだけで、LinuxでのSocketアプリと同じコードが動きます。

RaspberryPi用に作ってあったTCP Clientを移植してみました。
通信相手のSocketServerにはRaspberryPi(192.168.10.10)を使い、サーバー処理は受信した文字を、
小文字→大文字、大文字→小文字に変換して、TCP Clientに返すありふれたものです。
NetCATを使っても同じことができます。

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

task1(Clientタスク)
TCP Serverに「test」の文字を送信し、サーバーからの応答を表示しています。

task2(Dummyタスク)
task1の処理中、task1以外のタスクがどのようにブロックされるのかを確認するために動かしています。
Semaphoreを使って、task1のAPへの接続完了を待って動き始めます。
task1の終了で、このタスクも終了します。

/* 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 "esp8266.h"

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

#define SERVER_IP "192.168.10.10"
#define SERVER_PORT 9876

int TaskIsAlive = 1;
SemaphoreHandle_t xSemaphore;

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));
  }
}

// Socket Client 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);

  /* set up address to connect to */
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  //addr.sin_len = sizeof(addr);
  addr.sin_family = AF_INET;
  //addr.sin_port = PP_HTONS(SERVER_PORT);
  addr.sin_port = htons(SERVER_PORT);
  addr.sin_addr.s_addr = inet_addr(SERVER_IP);

  /* create the socket */
  int fd;
  int ret;
  fd = lwip_socket(AF_INET, SOCK_STREAM, 0);
  LWIP_ASSERT("fd >= 0", fd >= 0);

  /* connect */
  ret = lwip_connect(fd, (struct sockaddr*)&addr, sizeof(addr));
  /* should succeed */
  LWIP_ASSERT("ret == 0", ret == 0);

  /* write something */
  ret = lwip_write(fd, "test", 4);
  LWIP_ASSERT("ret == 4", ret == 4);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_write ret=%d\n",pcTaskGetName(0),nowTick,ret);

  /* read something */
  char buf[256];
  memset(buf,0,sizeof(buf));
  ret = lwip_read(fd, buf, sizeof(buf));
  LWIP_ASSERT("ret > 0", ret > 0);
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] lwip_read ret=%d\n",pcTaskGetName(0),nowTick,ret);
  if (ret > 0) {
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] ret=%d buf=%s\n",pcTaskGetName(0),nowTick,ret,buf);
  }

  /* close */
  ret = lwip_close(fd);
  LWIP_ASSERT("ret == 0", ret == 0);
  TaskIsAlive = 0;
  vTaskDelete( NULL );

}

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

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

  while(TaskIsAlive) {
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Running...\n",pcTaskGetName(0),nowTick);
    vTaskDelay(1);
  }
  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));

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

  xTaskCreate(task1, "Client", 384, NULL, 2, NULL);
  xTaskCreate(task2, "Dummy", 256, NULL, 2, NULL);

}

結果は以下のようになります。
438tick目でWiFi接続が完了したので、Dummyタスクが動き始めます。
441Tick目でClientタスクはSocketのread()待ちとなりますが、Dummyタスクは全くブロックされずに動き続けます。
SDK version:0.9.9
pcTaskGetName=uiT
mode : sta(18:fe:34:d4:2f:77)
add if0
[Client:1] Start
[Client:1] wifiStatus=1
[Dummy:1] Start
scandone
[Client:138] wifiStatus=1
add 0
aid 7
cnt

connected with aterm-e625c0-g, channel 11
dhcp client start...
[Client:238] wifiStatus=1
[Client:338] wifiStatus=1
ip:192.168.10.154,mask:255.255.255.0,gw:192.168.10.1
[Client:438] wifiStatus=5
[Client:438] Connected to AP
[Client] MAC address=18:fe:34:d4:2f:77
[Client] IP address=192.168.10.154
[Client] Netmask=255.255.255.0
[Client] Gateway=192.168.10.1
[Dummy:438] Take Semaphore
[Dummy:438] Running...
[Dummy:440] Running...
[Dummy:441] Running...
[Client:441] lwip_write ret=4
[Dummy:442] Running...
[Dummy:443] Running...
[Dummy:444] Running...
[Client:444] lwip_read ret=4
[Client:444] ret=4 buf=TEST

Server側はきちんと文字を受けとれています。
pi@raspberrypi ~/socket $ ./server
PORT=9876
接続を待っています
クライアントプログラムを動かして下さい
192.168.10.154 から接続を受けました
numrcv=4
Recv=[test]->Send=[TEST]
numrcv=0
接続を待っています
クライアントプログラムを動かして下さい

続く....