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

  • 2
    いいね
  • 0
    コメント

はじめに

この記事では,TOPPERS/R2CAによるWifi通信の方法について説明します.タスクは1個のみのシングルタスク版を説明します.

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

変更履歴

  • 2016/10/23
    • ファイルの変更
  • 2016/5/05
    • アクセスポイントの設定を別ファイルとした.
  • 2016/3/31
    • ESP8266のドライバの見直しによりサンプルプログラムを変更.
    • シングルタスク版について説明

ハードウエア

この記事で説明しているハードウェア以外にESP8266が必要です.

ESP8266のボードは多くの種類が出ています.基本的にはどれでも良いですが,動作確認したものは次の通りです.

  • スイッチサイエンス ESP-WROOM-02

    • ブレッドボードを使って接続しました.
  • NCES WiFi ボード

    • ESP8266をXbee互換の基盤にしたものです.
    • Arduino ワイヤレスプロトシールドや,NCES CAN シールドを使うと,簡単にArduinoと接続可能です.
    • ロゴから分かるようにTOPPERS/R2CA用に作成しました.

nces_can.jpg

サンプルのビルド

R2CAのパッケージの\examples\WifiEchoにあるサンプルを実行します.このサンプルはポート80に入力された文字列をそのまま返すエコーサーバーです.

まずexamples_gdef.hを開き,アクセスポイントの情報をSSIDとPASSWORDに設定します.

\examples\examples_gdef.h
#define STA_SSID       ""
#define STA_PASSWORD   ""

次にr2ca_app.hを開き,シングルタスク版にするためマクロをコメントアウトします.

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

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

実行

実行はdo_run.batをダブルクリックするとダウンロードが開始され,実行されます.Teratermには次のように表示されます.アクセスポイントに接続して,IPアドレスをDHCPから取得します.取得したアドレスはコンソールに出力されます.下記の例では,192.168.1.27となっています.

WS000000.JPG

PCから上記のIPのポート80に接続して適当な文字を入力してエンターを押すと入力した文字がそのまま送られて来ます.終了するには,Ctrl-Q入力してエンターを押します.

WS000001.JPG

ボード側のコンソールにもは,受信した旨が表示されます.
WS000002.JPG

プログラム

初期化処理

初期化処理ではアクセスポイントに接続してIPを取得しています.また,ポート番号80番でTCPサーバーを実行します.#define WMODE_STATION の定義をコメントアウトすると,アクセスポイントとして動作します.

\examples\WifiEcho\r2ca_app.cpp
void setup()
{
    int ret;

    Serial.println("Echo Server : Start!");

    ret = WiFi.begin(Serial5, 115200);

    if(ret == 1) {
        Serial.print("Cannot communicate with ESP8266.");
        while(1);        
    } else if(ret == 2) {
        Serial.println("FW Version mismatch.");
        Serial.print("FW Version:");
        Serial.println(WiFi.getVersion().c_str());
        Serial.print("Supported FW Version:");
        Serial.println(ESP8266_SUPPORT_VERSION);
        while(1);
    } else {
        Serial.print("begin ok\r\n");
    }

    Serial.print("FW Version:");
    Serial.println(WiFi.getVersion().c_str());

#ifdef  WMODE_STATION
    if (WiFi.setOprToStation()) {
        Serial.print("to station ok\r\n");
    } else {
        Serial.print("to station err\r\n");
    }

    if (wifi.joinAP(STA_SSID, STA_PASSWORD)) {
        Serial.print("Join AP success\r\n");
        Serial.print("IP: ");
        Serial.println(WiFi.getLocalIP().c_str());    
    } else {
        Serial.print("Join AP failure\r\n");
    }
#else /* !WMODE_STATION */ 
    if (WiFi.setOprToSoftAP()) {
        Serial.print("to softap ok\r\n");
    } else {
        Serial.print("to softap err\r\n");
    }    
    if(WiFi.setSoftAPParam(AP_SSID, AP_PASSWORD, 7, 0)){
        Serial.print("Set SoftAP success\r\n");
        Serial.print("IP: ");
        Serial.println(WiFi.getLocalIP().c_str());            
    }
    else {
        Serial.print("Set SoftAP failure\r\n");
    }
#endif /* WMODE_STATION */

    if (WiFi.enableMUX()) {
        Serial.print("multiple ok\r\n");
    } else {
        Serial.print("multiple err\r\n");
    }

    if (WiFi.startTCPServer(80)) {
        Serial.print("start tcp server ok\r\n");
    } else {
        Serial.print("start tcp server err\r\n");
    }

    if (WiFi.setTCPServerTimeout(60)) { 
        Serial.print("set tcp server timout 60 seconds\r\n");
    } else {
        Serial.print("set tcp server timout err\r\n");
    }

    Serial.print("setup end\r\n");

    setup_done = true;
}

エコーサーバー処理

まず,コネクションが変化したかをチェックします.コネクションは最大5個あります.このプログラムではコネクション0に対してデータを受け取り,エコーバックを行います.コネクション0に接続があれば,mux_idに0を設定します.

次に,コネクションが確立しているかをチェックして,80番のポートからデータを受け取ります.有効なデータを受け取った場合は,コンソールに出力した後,データをエコーバックしています.また,受信したデータをチェックして,Crtl-Qを受け取った場合は,終了処理も行います.

\examples\WifiEcho\r2ca_app.cpp
uint8_t mux_id_ptn;

uint8_t mux_id = MUX_NULL;
uint8_t task1_mux_id = MUX_NULL;

void loop()
{
    uint8_t buffer[128] = {0};
    uint8_t pre_mux_id_ptn;    
    uint32_t len;
    uint32_t i;

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

    if (mux_id == MUX_NULL) {
        return;
    }

    if (!WiFi.isConnected(mux_id)) {
        Serial.print("Echo Server : Port is closed: ");
        Serial.println(mux_id);
        mux_id = MUX_NULL;
        return;
    }

    if((len = WiFi.recv(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 : Close port : ");
            Serial.println(mux_id);            
            WiFi.releaseTCP(mux_id);
            mux_id = MUX_NULL;
            return;
        }
    }

    Serial.print("Echo Server : Recive Data from mux : ");
    Serial.println(mux_id);
    Serial.print("Echo Server : Recive len   : ");
    Serial.println(len);
    Serial.print("Echo Server : Recive Data  : ");
    Serial.println((char*)buffer);

    if(!WiFi.send(mux_id, buffer, len)) {
        Serial.println("Echo Server : send(mux_id, cmd) : Error!");
    }
}

API

ESP8266のライブラリはITEADLIB_Arduino_WeeESP8266をベースとしていますが,API幾つか増やしています.

    int begin(HardwareSerial &uart, uint32_t baud = 115200);
    bool kick(void);
    bool restart(void);
    String getVersion(void);
    bool setOprToStation(void);
    bool setOprToSoftAP(void);
    bool setOprToStationSoftAP(void);
    String getAPList(void);
    bool joinAP(String ssid, String pwd);
    bool leaveAP(void);
    bool setSoftAPParam(String ssid, String pwd, uint8_t chl = 7, uint8_t ecn = 4);
    String getJoinedDeviceIP(void);
    String getIPStatus(void);
    String getLocalIP(void);
    bool enableMUX(void);
    bool disableMUX(void);
    bool createTCP(String addr, uint32_t port);
    bool releaseTCP(void);
    bool registerUDP(String addr, uint32_t port);
    bool unregisterUDP(void);
    bool createTCP(uint8_t mux_id, String addr, uint32_t port);
    bool releaseTCP(uint8_t mux_id);
    bool registerUDP(uint8_t mux_id, String addr, uint32_t port);
    bool unregisterUDP(uint8_t mux_id);
    bool setTCPServerTimeout(uint32_t timeout = 180);
    bool startTCPServer(uint32_t port = 333);
    bool stopTCPServer(void);
    bool startServer(uint32_t port = 333);
    bool stopServer(void);
    bool send(const uint8_t *buffer, uint32_t len);
    bool send(uint8_t mux_id, const uint8_t *buffer, uint32_t len);
    bool send(String &str);
    bool send(uint8_t mux_id, String &str);
    uint32_t recv(uint8_t *buffer, uint32_t buffer_size, uint32_t timeout = 1000);
    uint32_t recv(uint8_t mux_id, uint8_t *buffer, uint32_t buffer_size, uint32_t timeout = 1000);
    uint32_t recv(uint8_t *coming_mux_id, uint8_t *buffer, uint32_t buffer_size, uint32_t timeout = 1000);
    bool isDataAvailable(uint8_t mux_id);
    bool isDataAvailable(void);
    bool isConnected(void);
    bool isConnected(uint8_t mux_id);
    bool getMuxCStatus(uint8_t *mux_id_ptn);

おわりに

ESP8266を使うと安価に簡単にネットに接続できます.TOPPERS/R2CAを使うと,マルチタスクが使えるので,サーバーで接続待ちになりながら,センサー等の処理を同時に実行出来ます.