Arduino M0 Pro でマルチタスクプログラミング : Wifi通信(マルチタスク)

# はじめに

この記事では，[TOPPERS/R2CA](http://dev.toppers.jp/trac_user/contrib/wiki/rtos_arduino)による[Wifi通信](http://qiita.com/exshonda/items/914b7df0efd36866fd98)の続きとして，マルチタスクによる送受信方法について説明します．この例ではタスクは2個使用しますが，ESP8266では原理的には最大5個のタスクで送受信が可能です．

TOPPERS/R2CAの説明とサンプルの動作方法は次の記事を見て下さい．

 - [Arduino M0 Pro でマルチタスクプログラミング : インストールとサンプルの実行](http://qiita.com/exshonda/items/093870ecdcda6c79678c)
 - [Arduino M0 Pro でマルチタスクプログラミング : マルチタスク](http://qiita.com/exshonda/items/7afb12b56696cf44895e)
 - [Arduino M0 Pro でマルチタスクプログラミング  : Wifi通信](http://qiita.com/exshonda/items/914b7df0efd36866fd98)


# ハードウエア

[Arduino M0 Pro でマルチタスクプログラミング  : Wifi通信](http://qiita.com/exshonda/items/914b7df0efd36866fd98)で紹介してるハードウェアをそろえて下さい．

#サンプルのビルド

R2CAのパッケージの\examples\WifiEchoにあるサンプルを実行します．このサンプルはポート80に入力された文字列をそのまま返すエコーサーバーです．SSIDとPASSWORDは前の記事と同様に設定します．

マルチタスクバージョンでは，rca_app.hで定義されいるMULTI_ECHO_SERVERを有効にします．

```\examples\WifiEcho\r2ca_app.h
#define MULTI_ECHO_SERVER
```

ビルドは同じフォルダにあるdo_make.bat をダブルクリックすると開始されます．ファイルが色々作成されますが，rca.elfが出来るとビルド成功です．

#実行

実行はdo_run.batをダブルクリックするとダウンロードが開始され，実行されます．Teratermには次のように表示されます．シングルタスク版との違いは，"Echo Server Task1 : start"の箇所で，タスクがもう一つ動作しています．

![WS000003.JPG](https://qiita-image-store.s3.amazonaws.com/0/106870/7051865c-e6e6-99fe-3ad6-ef7efbc31b64.jpeg)

PCから上記のIPのポート80に接続して適当な文字を入力してエンターを押すと入力した文字がそのまま送られて来ます．終了するには，Ctrl-Q入力してエンターを押します．シングルタスク版との違いとしては，同時に2個のクライアントからの接続を受け付け可能です．

下記の例では，2個のtelnetをポート80に接続して，それぞれ"ff"と"aa"を送信しています．"ff"の方がメインタスク，"aa"の方はTask1が受け取っていることが分かります．

![WS000004.JPG](https://qiita-image-store.s3.amazonaws.com/0/106870/fd3d7621-b6c3-908b-941d-738867d204cd.jpeg)


#プログラム


##エコーサーバー処理

処理本体を見ていきます．メインタスクのloop()では，実行開始時にdelay(1)を呼び出しています．これもTask1と処理を切り替えるためです．

次にコネクションの状態をチェックして，1番のコネクションに接続があった場合は，task1_mux_idに'1'を設定して，Task1に処理を依頼します．

```\examples\Basic\r2ca_app.cpp

uint8_t mux_id = MUX_NULL;
uint8_t task1_mux_id = MUX_NULL;

void loop()
{
.........<skip>.............

    delay(1);

    /* Check Connection Status */
    pre_mux_id_ptn = mux_id_ptn;

    if(!WiFi.getMuxCStatus(&mux_id_ptn)) {
        Serial.println("getMuxCStatus(&mux_id_ptn) : Error!");
    }
    else {        
        if (pre_mux_id_ptn != mux_id_ptn) {
            Serial.print("Connection Status changed! : 0x");
            Serial.println(mux_id_ptn, HEX);
            if (mux_id_ptn & 0x01) {
                mux_id = 0; 
            }
            if (mux_id_ptn & 0x02) {
                task1_mux_id = 1;
            }
        }
    }

.........<skip>.............
}

```

Task1のメイン処理を見ます．処理内容はメインタスクの後半の処理とほぼ同様です．mux_idを引数とした関数を作った方がよいかもしれません．

task1_mux_idに有効な値が設定されたら，受信とエコーバックを開始します．ここにもdelay(1)を入れています．

```\examples\Basic\r2ca_app.cpp
#ifdef MULTI_ECHO_SERVER
void loop1() {
    uint8_t buffer[128] = {0};
    uint32_t len;
    uint32_t i;

    delay(1);

    if (task1_mux_id == MUX_NULL) {
        delay(1);
        return;
    }
    
    if (!WiFi.isConnected(task1_mux_id)) {
        Serial.print("Echo Server Task1 : Port is closed: ");
        Serial.println(task1_mux_id);
        task1_mux_id = MUX_NULL;
        return;
    }
    if((len = WiFi.recv(task1_mux_id, buffer, sizeof(buffer))) == 0) {
        return;
    }        

    /* Recived Data */

    for(i = 0; i < len; i++) {
        /* If Recive Ctrl-q(17) */
        if(buffer[i] == 17) {
            Serial.print("Echo Server Task1 : Close port : ");
            Serial.println(task1_mux_id);            
            WiFi.releaseTCP(task1_mux_id);
            task1_mux_id = MUX_NULL;
            return;
        }
    }

    Serial.print("Echo Server Task1 : Recive Data from mux : ");
    Serial.println(task1_mux_id);
    Serial.print("Echo Server Task1 : Recive len   : ");
    Serial.println(len);
    Serial.print("Echo Server Task1 : Recive Data  : ");
    Serial.println((char*)buffer);
    
    if(!WiFi.send(task1_mux_id, buffer, len)) {
        Serial.println("Echo Server Task1 : send(task1_mux_id, cmd) : Error!");
    }
}
#endif /* MULTI_ECHO_SERVER */
```

# おわりに

TOPPERS/R2CAを使うと簡単にマルチタスクによる送受信が可能になることが分かってもらえたかと思います．ラウンドロビンスケジューリングでないので，delay(1)を適宜記述する必要があります．面倒な場合は，以下のマクロをrca_app.hに追加することで，1m周期のラウンドロビンスケジューリング実現できます．詳細は別途記事を書きます．

```\examples\WifiEcho\r2ca_app.h
#define RCA_RR_SCHEDULE 0xffffff
#define RCA_RR_SCHEDULE_CYCLE 1
```