esp-open-rtosを使ってみる

TCP Server(Socket-API)

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

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

RaspberryPiで作ってあったTCP Serverを移植してみました。
受信した文字を、小文字→大文字、大文字→小文字に変換して、TCP Clientに返すありふれたものです。
NetCatを使っても同じことができます。

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

task1(WiFiタスク)
APへの接続を監視するタスクです。
APへの接続が完了したらSemaphoreを使ってTask2に許可を与えます。

task2(Serverタスク)
TCP Clientからの接続を待って、データーを受信します。ポート番号には9876を使います。
受信データを変換してTCP Clientに応答します。

/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include <string.h>
#include <ctype.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_PORT 9876

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 Server 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 connect to */
  struct sockaddr_in srcAddr;
  struct sockaddr_in dstAddr;
  memset(&srcAddr, 0, sizeof(srcAddr));
  //srcAddr.sin_len = sizeof(srcAddr);
  srcAddr.sin_family = AF_INET;
  //srcAddr.sin_port = PP_HTONS(SERVER_PORT);
  srcAddr.sin_port = htons(SERVER_PORT);
  srcAddr.sin_addr.s_addr = INADDR_ANY;

  /* create the socket */
  int srcSocket;
  int dstSocket;
  socklen_t dstAddrSize;
  int ret;
  int numrcv;

  srcSocket = lwip_socket(AF_INET, SOCK_STREAM, 0);
  LWIP_ASSERT("srcSocket >= 0", srcSocket >= 0);

  /* bind socket */
  ret = lwip_bind(srcSocket, (struct sockaddr *)&srcAddr, sizeof(srcAddr));
  /* should succeed */
  LWIP_ASSERT("ret == 0", ret == 0);

  /* listen socket */
  ret = lwip_listen(srcSocket, 5);
  /* should succeed */
  LWIP_ASSERT("ret == 0", ret == 0);

  char buf[1024];
  while(1) {
    // 接続の受付け
    printf("[%s:%d] 接続を待っています クライアントプログラムを動かして下さい\n",pcTaskGetName(0),nowTick);
    dstAddrSize = sizeof(dstAddr);
    dstSocket = lwip_accept(srcSocket, (struct sockaddr *)&dstAddr, &dstAddrSize);
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] %s から接続を受けました\n",pcTaskGetName(0),nowTick,inet_ntoa(dstAddr.sin_addr));

    while(1) { // クライアントがSocketをクローズしてからこちらもクローズする
      /* read something */
      memset(buf,0,sizeof(buf));
      numrcv = lwip_read(dstSocket, buf, 1024);
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] numrcv=%d\n",pcTaskGetName(0),nowTick,numrcv);
      if(numrcv ==0 || numrcv ==-1 ){ // client close socket
        lwip_close(dstSocket); break;
      }
      printf("Recv=[%s]",buf);
      for (int i=0; i< numrcv; i++){ // bufの中の小文字を大文字に変換
        if(isalpha((int)buf[i])) {
          if(islower((int)buf[i])) {
            buf[i] = toupper((int)buf[i]);
          } else {
            buf[i] = tolower((int)buf[i]);
          }
        }
      }
      /* write something */
      ret = lwip_write(dstSocket, buf, numrcv);
      LWIP_ASSERT("ret == numrcv", ret == numrcv);
      printf("->Send=[%s]\n",buf);
    } // end while
  } // end for


  /* close (never come here) */
  ret = lwip_close(srcSocket);
  LWIP_ASSERT("ret == 0", ret == 0);
  vTaskDelete( NULL );

}

// 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);
  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, "WiFi", 256, NULL, 2, NULL);
  xTaskCreate(task2, "Server", 512, NULL, 2, NULL);
}

結果は以下のようになります。
438tick目でWiFi接続が完了したので、Serverタスクが動き始めます。
これ以降、ServerタスクがClientを待ち受けます。
SDK version:0.9.9
pcTaskGetName=uiT
mode : sta(18:fe:34:d4:2f:77)
add if0
[WiFi:1] Start
[WiFi:1] wifiStatus=1
[Server:1] Start
scandone
[WiFi:138] wifiStatus=1
add 0
aid 7
cnt

connected with aterm-e625c0-g, channel 11
dhcp client start...
[WiFi:238] wifiStatus=1
[WiFi:338] wifiStatus=1
ip:192.168.10.154,mask:255.255.255.0,gw:192.168.10.1
[WiFi:438] wifiStatus=5
[WiFi:438] Connected to AP
[WiFi] MAC address=18:fe:34:d4:2f:77
[WiFi] IP address=192.168.10.154
[WiFi] Netmask=255.255.255.0
[WiFi] Gateway=192.168.10.1
[Server:438] Take
[Server:438] 接続を待っています クライアントプログラムを動かして下さい
[Server:1335] 192.168.10.10 から接続を受けました
[Server:1335] numrcv=25
Recv=[data from raspberry 00000]->Send=[DATA FROM RASPBERRY 00000]
[Server:1336] numrcv=0
[Server:1336] 接続を待っています クライアントプログラムを動かして下さい
[Server:2262] 192.168.10.10 から接続を受けました
[Server:2262] numrcv=25
Recv=[data from raspberry 00000]->Send=[DATA FROM RASPBERRY 00000]
[Server:2266] numrcv=0
[Server:2266] 接続を待っています クライアントプログラムを動かして下さい

Client側はきちんと応答を受けとれています。
pi@raspberrypi ~/socket $ ./client 1
サーバーマシンのIPは?:192.168.10.154
Connecting...dstSocket=3
192.168.10.154 に接続しました
Send=[data from raspberry 00000]→Recv=[DATA FROM RASPBERRY 00000]
loop=1 difftime=5.000000[sec] 0.083333[min] retry=0
pi@raspberrypi ~/socket $ ./client 1
サーバーマシンのIPは?:192.168.10.154
Connecting...dstSocket=3
192.168.10.154 に接続しました
Send=[data from raspberry 00000]→Recv=[DATA FROM RASPBERRY 00000]
loop=1 difftime=5.000000[sec] 0.083333[min] retry=0

続く....