STM32F103を使ってみる

TCP/IP通信


Ethernet_STMライブラリ+W5500モジュールを使ったTCP/IP通信を紹介します。
こ ちらで紹介しているArduinoのSocket通信のスケッチを元に、
クライアント、サーバーともライブラリだけを変えて書きこんでみましたが、サーバー側の動きが違います。


バグなのか仕様なのか分かりませんが、一度に受信しているデータの長さがまちまちで、全てのデータを1回では受信できません。
クライアント側は「data from arduino 00000」の23文字を送り出していますが、サーバー側はこれを何回かに分けて受信しています。

この類の症状はパケットが込み合っている企業ネットワークではよくあることですが、
自宅のネットワークで起こるというのはライブラリのバグっぽいです。

回避方法は簡単で、クライアント側は最初に全体の文字数を送り、その後データを送ります。
そのように修正したクライアント側のスケッチは以下の様になります。
/*
  * W5500 Ethernet Module Socket Client example.
  */

#include <SPI.h>
#include <Ethernet_STM.h>   // https://github.com/rogerclarkmelbourne/Arduino_STM32

#define INTERVAL       5000
#define SOCKET_HOST    "192.168.10.190" // You have to change
#define SOCKET_PORT    9876             // You have to change
#define TIME_OUT       10000

//#define MODULE         "ENC28J60"
#define MODULE         "W5500"

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
#if defined(WIZ550io_WITH_MACADDRESS) // Use assigned MAC address of WIZ550io
;
#else
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
#endif 

EthernetClient client;

unsigned long nextMillis;

void setup() {
  delay(1000);Serial.begin(9600);

  // start Ethernet and UDP
#if defined(WIZ550io_WITH_MACADDRESS)
  if (Ethernet.begin() == 0) {
#else
  if (Ethernet.begin(mac) == 0) {
#endif 
    Serial.println("Failed to configure Ethernet using DHCP");
    while(1) {}
  } 
  Serial.print("My IP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("Netmask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("GW IP: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("DNS IP: ");
  Serial.println(Ethernet.dnsServerIP());
  Serial.print("localIP: ");

#if defined(WIZ550io_WITH_MACADDRESS)
  byte mac_address[6] ={0,};
  W5100.getMACAddress(mac_address);
  Serial.print("MAC: ");
  for(int i = 0; i < 6; i++) {
    Serial.print("0x");
    Serial.print(mac_address[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif 

  nextMillis = millis();
}


void loop() {
  static int num = 0;
  char smsg[30];
  char rmsg[30];
  int rflag;
  unsigned long timeout;
  unsigned long now;
 
  now = millis();
  if ( long(now - nextMillis) > 0) {
    nextMillis = millis() + INTERVAL;
    Serial.print("Client connect....");
    if (!client.connect(SOCKET_HOST, SOCKET_PORT)) {
      Serial.println("failed");
    } else {
      Serial.println("ok");
      sprintf(&smsg[1],"data from arduino %05d",num);
      int len=strlen(&smsg[1]);
      Serial.println("len=" + String(len));
      smsg[0]=len; // 最初に全体の長さを送る
      num++;
      client.write(smsg, len+1);

      // wait for responce
      rflag = 1;
      timeout = millis();
      while(client.available() == 0) {
        now = millis();
//        Serial.println("now="+String(now));
//        Serial.println("timeout="+String(timeout));
        if (long(now - timeout) > TIME_OUT) {
          rflag = 0;
        }
      } // end while

      Serial.print("Server response....");
      if (rflag == 0) {
        Serial.println("failed");
      } else {
        Serial.println("ok");
        int size;
        while((size = client.available()) > 0) {
          Serial.println("[" + String(MODULE) + " Socket Client]Receive Size=" + String(size));
          size = client.read((uint8_t *)rmsg,size);
          Serial.write((uint8_t *)&smsg[1],size-1);
          Serial.write("->");
          Serial.write((uint8_t *)rmsg,size);
          Serial.println("");
        } // end while
      }

      //disconnect client
      Serial.println("Client disconnect");
      client.stop();
    } // end if
  } // end if
}



Socket通信のサーバー側のスケッチは以下の様になります。
サーバー側は最初に1文字だけ受信して全体の文字数を取り出し、その後、文字数を満足するまで読み込みを繰り返します。
クライアントから受け取った文字は小文字→大文字に変換してクライアントに応答します。
/*
 * W5500 Ethernet Module TcpServer example.
 */

#include <SPI.h>
#include <Ethernet_STM.h>   // https://github.com/rogerclarkmelbourne/Arduino_STM32

#define SOCKET_PORT    9876             // You have to change
//#define MODULE         "ENC28J60"
#define MODULE         "W5500"

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
#if defined(WIZ550io_WITH_MACADDRESS) // Use assigned MAC address of WIZ550io
;
#else
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
#endif 

EthernetServer server = EthernetServer(SOCKET_PORT);

void setup()
{
  byte myIP[] = { 192, 168, 10, 190 };

  delay(1000);Serial.begin(9600);
  Serial.print("Ethernet begin....");
  // start Ethernet and UDP
#if defined(WIZ550io_WITH_MACADDRESS)
  Ethernet.begin(myIP);
#else
  Ethernet.begin(mac,myIP);
#endif

  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
//    Serial.println(Ethernet.localIP()[thisByte]);
    if (Ethernet.localIP()[thisByte] != myIP[thisByte] ) {
      Serial.println("fail....");
      while (1) {}
    }
  }
  Serial.print("My IP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("Netmask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("GW IP: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("DNS IP: ");
  Serial.println(Ethernet.dnsServerIP());
  Serial.print("localIP: ");

#if defined(WIZ550io_WITH_MACADDRESS)
  byte mac_address[6] ={0,};
  W5100.getMACAddress(mac_address);
  Serial.print("MAC: ");
  for(int i = 0; i < 6; i++) {
    Serial.print("0x");
    Serial.print(mac_address[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif

  server.begin();
  Serial.println("Server Start");
}

void loop()
{
  EthernetClient client;
  uint8_t wk;
 
  if (client = server.available()) {
    while(client.available() > 0) {
      wk = client.read(); // 全体の長さ
      size_t tsize = wk;
      Serial.println("[" + String(MODULE) + " Socket Server]Receive tsize=" + String(tsize));
      char* rmsg = (char *)malloc(tsize+1);
      char* smsg = (char*)malloc(tsize+1);
      memset(rmsg,0,tsize+1);
      memset(smsg,0,tsize+1);
      size_t rsize =0;
      int ofs = 0;
      while(1) {
        if (client.available() <= 0) continue;
        wk = client.read();
        rsize++;
//        Serial.println("rsize=" + String(rsize));
//        Serial.write((uint8_t *)tbuf,psize);
//        Serial.println("]");
        rmsg[ofs] = wk;
        if(isalpha(wk)) {
          smsg[ofs++] = toupper(wk);
        } else {
          smsg[ofs++] = wk;
        }
//        Serial.println("smsg=[" + String(smsg) + "]");
        if (rsize == tsize) break; // すべてのデータを受信した
      }
      Serial.print(rmsg);
      Serial.print("->");
      Serial.println(smsg);

      client.write((uint8_t *)smsg,tsize);
      free(rmsg);
      free(smsg);
    } // end while
    client.stop();
  }
}



クライアント側のIDEにはこのように表示されます。
サーバーからクライアントへの応答は長さを付加せずにデータだけを送っていますが、なぜかクライアント側は一発で全てのデータを取れています。
クライアント→サーバー:全体の長さ+データ
サーバー→クライアント:データだけ
やっぱりサーバー側ライブラリ(受信処理)のバグっぽいです。


サーバー側のIDEにはこのように表示されます。


続く...