PlatformIOでESP32を開発する

TCP Client & Server


PlatformIO for ESP32ではWiFiオブジェクトを使ってAPに接続することができます。
Arduino-IDEでは、WiFiClientやWiFiServerオブジェクトを使ってTCP/IP通信を行いますが、
PlatformIO for ESP32にはlwIPが組み込まれているので、以下のAPIを使うことができます。
・raw API(callback API)
・Sequential-style API(Netconn APIとNETIF API)
・Socket API(BSD-Socket API)

そこで、WiFiオブジェクトを使ってAPに接続し、Socket APIを使ったTCP/IPのClientタスクとServerタスクを同時に動かしてみました。

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

SERVER_IP、SEND_PORT、RECEIVE_PORT、SSID、PASSWORDは適当に変更してください。
#include "freertos/FreeRTOS.h"
#include "Arduino.h"
#include "WiFi.h"
#include "ESPmDNS.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 SEND_PORT 9876
#define RECEIVE_PORT 9877

const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "PASSWORD";

// https://qiita.com/koara-local/items/585755faac70c8b37b5b
// [C++11 ~] variadic templates
template <typename ... Args>
void _printf(const char *format, Args const & ... args) {
    // int printf(const char *format, ...);
    TickType_t _nowTick = xTaskGetTickCount();
    char * _taskName = pcTaskGetTaskName( NULL );
    printf("[%s:%d] ",_taskName, _nowTick);
    printf(format, args ...);
}

#if 0
// [C/C++] stdarg
void _printf(const char *format, ...) {
    va_list va;
    va_start(va, format);
    // int vprintf(const char *format, va_list ap);
    TickType_t _nowTick = xTaskGetTickCount();
    char * _taskName = pcTaskGetTaskName( NULL );
    printf("[%s:%d] ",_taskName, _nowTick);
    vprintf(format, va);
    va_end(va);
}
#endif

void showNetworkInfo() {
    IPAddress ip = WiFi.localIP();
    IPAddress mk = WiFi.subnetMask();
    IPAddress gw = WiFi.gatewayIP();
    _printf("IP address=%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
    _printf("Netmask   =%d.%d.%d.%d\n", mk[0], mk[1], mk[2], mk[3]);
    _printf("Gateway   =%d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]);
}

// TCP-IP Server Task
void task1(void *pvParameters)
{
    //TickType_t nowTick;
    //nowTick = xTaskGetTickCount();
    _printf("start Priority=%d\n",uxTaskPriorityGet( NULL ));
    //printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);

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

    /* 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(RECEIVE_PORT);
    srcAddr.sin_port = htons(RECEIVE_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("%d で接続を待っています クライアントプログラムを動かして下さい\n",RECEIVE_PORT);
      dstAddrSize = sizeof(dstAddr);
      dstSocket = lwip_accept(srcSocket, (struct sockaddr *)&dstAddr, &dstAddrSize);
      //nowTick = xTaskGetTickCount();
      _printf("%s から接続を受けました\n",inet_ntoa(dstAddr.sin_addr));

      while(1) { // クライアントがSocketをクローズしてからこちらもクローズする
        /* read something */
        memset(buf,0,sizeof(buf));
        numrcv = lwip_read(dstSocket, buf, 1024);
        //nowTick = xTaskGetTickCount();
        _printf("numrcv=%d\n",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 );
}


// TCP-IP Client Task
void task2(void *pvParameter)
{
    _printf("start Priority=%d\n",uxTaskPriorityGet( NULL ));
    /* 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(SEND_PORT);
    addr.sin_port = htons(SEND_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);

    char buf[256];
    for(int i=0;i<10;i++) {

      /* write something */
      sprintf(buf,"Hello from PlatformIO %02d",i);
      //sprintf(buf,"test %02d",i);
      ret = lwip_write(fd, buf, strlen(buf));
      _printf("lwip_write ret=%d\n",ret);
      LWIP_ASSERT("ret == strlen(buf)", ret == strlen(buf));

      /* read something */
      memset(buf,0,sizeof(buf));
      ret = lwip_read(fd, buf, sizeof(buf));
      LWIP_ASSERT("ret > 0", ret > 0);
      _printf("lwip_read ret=%d\n",ret);
      if (ret > 0) {
        _printf("ret=%d buf=%s\n",ret,buf);
      }

      vTaskDelay(10000 / portTICK_PERIOD_MS);

    } // end for

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

void setup() {
    vTaskDelay(2000 / portTICK_PERIOD_MS);
    _printf("start Priority=%d\n",uxTaskPriorityGet( NULL ));
    _printf("portTICK_PERIOD_MS=%d\n",portTICK_PERIOD_MS);

    /* Connect WiFi */
    _printf("Connecting to %s\n",WIFI_SSID);
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASS);

    while (WiFi.status() != WL_CONNECTED) {
      vTaskDelay(5000 / portTICK_PERIOD_MS);
    }

    _printf("WiFi connected\n");
    showNetworkInfo();

    /* Start mDNS responder */
    if (!MDNS.begin("esp32")) {
      while(1) {
        _printf("Error setting up MDNS responder!");
        vTaskDelay(5000 / portTICK_PERIOD_MS);
      }
    }
    _printf("mDNS responder started");

    /* Start TCP-IP Server */
    xTaskCreatePinnedToCore(task1, "Server", 4096, NULL, 1, NULL, tskNO_AFFINITY);
    /* Start TCP-IP Client */
    xTaskCreatePinnedToCore(task2, "Client", 4096, NULL, 1, NULL, tskNO_AFFINITY);

    /* stop loop task */
    vTaskDelete( NULL );
}

void loop() { // Never run
    _printf("loop\n");
}

実行するとWiFi接続後にRaspberryPi側との通信を始めます。
ClientとServerが同時に動いています。


Raspberry側のサーバタスクもきちんとデーターを受け取れています。


setup関数の中でmDNS Responderを実行しているので、名前を使ってアドレス解決をすることができます。


このアドレスに向けてncを使って送信すると、応答が返ってきます。


続く....