本チュートリアルでは、iSDIOコマンドを使って、FlashAirの無線LANをAPモードで起動してみます。

概要

APモードの起動には、iSDIOで規定されているEstablishコマンドを使用します。

Establishコマンドの発行が完了した後、 FlashAirが実際に無線LAN APとして動作開始するまでには、 数秒~20秒程度の時間がかかります。 動作開始を確認するためには、ステータスレジスタを利用します。

チュートリアル3で ステータスレジスタの読み出し方を解説しましたが、 全部のステータスは必要ありませんので、 本チュートリアルでは、コマンド発行後の処理ステータスを確認する方法だけを切り出してみます。

また、Establishコマンドが正しく動作するためには、 FlashAirの無線LANが未接続状態になっていなければなりません。 そこで、このチュートリアルでは、無線LANを切断するためのDisconnectコマンドも実行してみましょう。

以下、 チュートリアル3のソースコードを元に追加・変更していきます。

シーケンスIDについて

前述のとおり、コマンドは発行してから処理が完了するまで時間がかかります。 最後に発行されたコマンドを識別するために、シーケンスIDという仕組みが取り入れられています。

iSDIOのシーケンスIDは、0以上の4バイトの符号なし数値です。 コマンドを新しく発行するたびに、シーケンスIDを新しい値(通常は以前の値に1加えたもの)に更新します。

注意点:シーケンスIDは、ホストプログラムだけが変更するとは限りません。

FlashAirの起動時あるいはCGIでの指示により、FlashAir自身が無線LAN状態を変更した場合も、 iSDIOコマンドが発行されてシーケンスIDが変更される可能性があることに注意が必要です。

そのため、厳密にはコマンド発行の都度最新のシーケンスIDを取得して、 新しいシーケンスIDを作成するのが正しいでしょう。 ただし、本チュートリアルシリーズでは説明の簡略化のため、起動直後のシーケンスIDを読み取り、 それ以降は自分で管理したIDのみを使います。

自分が発行した最後のシーケンスIDを覚えておくため、変数を一つ用意します。

arduino_tutorial_04.ino (一部抜粋)

uint32_t nextSequenceId = 0;

Establishコマンドの発行

Establishは、無線LAN機能をAPモードで立ち上げるためのコマンドです。 同時に、HTTPサーバー機能とDHCPサーバー機能も立ち上がります。

詳細は、SD Specifications Part E7 Wireless LAN Simplified Addendum Version 1.10 4.2.3 Estalibsh(ssid, networkKey, encMode) に規定されています。

コマンドデータを作るためには、下記の情報が必要です。

  • コマンドID (3)
  • シーケンスID
  • 引数個数 (3)
  • 引数
    • SSID
    • ネットワークキー (パスワード)
    • セキュリティモード

arduino_tutorial_04.ino (一部抜粋)

boolean iSDIO_establish(uint32_t sequenceId) {
  Serial.print(F("\nEstablish command: \n"));
  memset(buffer, 0, 512);
  uint8_t* p = buffer;
  p = put_command_header(p, 1, 0);
  p = put_command_info_header(p, 0x03, sequenceId, 3);
  p = put_str_arg(p, "sdiotest");
  p = put_str_arg(p, "12345678");
  p = put_u8_arg(p, 0x06);
  put_command_header(buffer, 1, (p - buffer));
  printHex(buffer, (p - buffer));
  return card.writeExtDataPort(1, 1, 0x000, buffer) ? true : false;
}
  • 3行目~10行目 FlashAirに発行するコマンドのデータをArduinoのメモリ上に作っています。
    • 5行目 ヘルパー関数を利用して、コマンドのヘッダを作っています。 最後の引数にはコマンドデータの長さ(バイト数)を入れますが、 可変長の引数があるため、引数を含んでいるため、後で計算します。
    • 6行目 ヘルパー関数を利用して、コマンド情報のヘッダを作っています。 establishのコマンドID0x03、シーケンスIDsequenceID、引数の個数3を指定しています。
    • 7行目 1番目の引数SSIDを書き込んでいます。 文字列引数を書き込むヘルパー関数を利用しています。
    • 8行目 同様に、2番目の引数ネットワークキーを書き込んでいます。
    • 9行目 3番目の引数セキュリティモードを書き込んでいます。 ここでは、WPA2-PSK and AESを表す0x06を指定しています。他の値については仕様書をご覧ください。 1バイトデータを書き込むヘルパー関数を利用しています。
    • 10行目 全データが書き込まれ、バイト数が確定したので、コマンドヘッダを改めて書き込んでいます。
    • 11行目 正しく作成されたかを確認する目的で、コマンドデータをダンプしています。 printHex()関数についてはサンプルコードを参照ください。
  • 12行目 FlashAirに対してデータを書き込んでいます。

コマンド処理状況の確認

コマンド処理状況を確認するには、iSDIOのコマンドレスポンスステータスを読み取ります。

コマンドレスポンスステータス (SD Specifications Part E7 iSDIO Simplifed Specification Version 1.10より抜粋)

コマンドレスポンスステータス

特に重要なのは、iSDIO command sequence idと、Response Statusの2つです。 iSDIO command sequence idが、いま確認したいシーケンスIDと一致しているか確認します。 一致していることが確認できたら、Response Statusで処理状況を確認します。

なお、iSDIO規格では最大8つまでのコマンドを同時に発行できることになっており、 コマンドレスポンスステータスレジスタも8個ありますが、 FlashAirは1つまでの対応となっていますので、アドレス0x440のステータスを常に読み取ります。

以下に、指定したシーケンスIDのコマンドの終了を待機する関数を作成します。

arduino_tutorial_04.ino (一部抜粋)

boolean iSDIO_waitResponse(uint32_t sequenceId) {
  Serial.print(F("\nWaiting response "));
  uint8_t prev = 0xFF;
  for (int i = 0; i < 20; ++i) {
    memset(buffer, 0, 0x14);

    // Read command response status.
    if (!card.readExtMemory(1, 1, 0x440, 0x14, buffer)) {
      return false;
    }

    uint8_t resp = get_u8(buffer + 8);
    if (sequenceId == get_u32(buffer + 4)) {
     if (prev != resp) {
        switch (resp) {
          case 0x00:
            Serial.print(F("\n  Initial"));
            break;
          case 0x01:
            Serial.print(F("\n  Command Processing"));
            break;
          case 0x02:
            Serial.println(F("\n  Command Rejected"));
            return false;
          case 0x03:
            Serial.println(F("\n  Process Succeeded"));
            return true;
          case 0x04:
            Serial.println(F("\n  Process Terminated"));
            return false;
          default:
            Serial.print(F("\n  Process Failed "));
            Serial.println(resp, HEX);
            return false;
        }
        prev = resp;
      }
    }
    Serial.print(F("."));
    delay(1000);
  }
  return false;
}
  • 8行目 コマンドレスポンスステータスレジスタを読み取っています。
  • 13行目 シーケンスIDが一致することを確認しています。
  • 15行目~35行目 レスポンスステータスを確認してます。処理が完了すると、Process Succeededになります。
  • 39行目 1秒ごとにこの確認を実行しています。

Disconnectコマンドの発行

Disconnectは、無線LANを切断するためのコマンドです。 同時に、HTTPサーバー機能とDHCPサーバー機能も停止します。

詳細は、SD Specifications Part E7 Wireless LAN Simplified Addendum Version 1.10 4.2.7 Disconnect(ssid, networkKey, encMode) に規定されています。

コマンドデータを作るためには、下記の情報が必要です。

  • コマンドID (7)
  • シーケンスID
  • 引数個数 (0)
  • 引数はありません。

arduino_tutorial_04.ino (一部抜粋)

boolean iSDIO_disconnect(uint32_t sequenceId) {
  Serial.print(F("\nDisconnect command: \n"));
  memset(buffer, 0, 512);
  uint8_t* p = buffer;
  p = put_command_header(p, 1, 0);
  p = put_command_info_header(p, 0x07, sequenceId, 0);
  put_command_header(buffer, 1, (p - buffer));
  printHex(buffer, (p - buffer));
  return card.writeExtDataPort(1, 1, 0x000, buffer) ? true : false;
}

メインプログラム

メインプログラムは、対話式のプログラムとしてみましょう。

Arduinoのシリアルターミナルで入力されたコマンド番号に従って、ステータス表示、Disconnect、Establish の各コマンドを実行します。

arduino_tutorial_04.ino (一部抜粋)

void loop() {
  if (!iSDIO_status()) {
    Serial.println(F("\nFailed to read status."));
  }

  Serial.print(F("\n0. Show status"));
  Serial.print(F("\n1. Disconnect"));
  Serial.print(F("\n2. Establish"));
  Serial.print(F("\n\nCommand? (next sequence id = "));
  Serial.print(nextSequenceId, DEC);
  Serial.println(F(")"));

  while (Serial.available() == 0);
  char command = Serial.read();

  switch (command - '0') {
    case 0 :
      break;
    case 1 :
      if (iSDIO_disconnect(nextSequenceId) &&
          iSDIO_waitResponse(nextSequenceId)) {
        Serial.println(F("\nSuccess."));
      } else {
        Serial.print(F("\nFailed or waiting. errorCode="));
        Serial.println(card.errorCode(), HEX);
      }
      nextSequenceId++;
      break;
    case 2 :
      if (iSDIO_establish(nextSequenceId) &&
          iSDIO_waitResponse(nextSequenceId)) {
        Serial.println(F("\nSuccess."));
      } else {
        Serial.print(F("\nFailed or waiting. errorCode="));
        Serial.println(card.errorCode(), HEX);
      }
      nextSequenceId++;
      break;
    default :
      Serial.println(F("\nUnknown command."));
      break;
  }
}
  • 2行目 ステータス表示です。
  • 6行目~11行目 実行できるコマンドおよび次のシーケンスIDを表示しています。
  • 13行目~14行目 番号の入力を待っています。
  • 16行目~42行目 入力された番号に従ってコマンドを実行します。

ステータス表示をloop()関数に含めましたので、setup()関数からは削除しましょう。

arduino_tutorial_04.ino (一部抜粋)

void setup() {
  // Initialize UART for message print.
  Serial.begin(9600);
  while (!Serial) {
    ;
  }

  // Initialize SD card.
  Serial.print(F("\nInitializing SD card..."));  
  if (card.init(SPI_HALF_SPEED, chipSelectPin)) {
    Serial.print(F("OK"));
  } else {
    Serial.print(F("NG"));
    abort();
  }

  // Read the previous sequence ID.
  if (card.readExtMemory(1, 1, 0x420, 0x34, buffer)) {
    if (buffer[0x20] == 0x01) {
      nextSequenceId = get_u32(buffer + 0x24);
      iSDIO_waitResponse(nextSequenceId);
      nextSequenceId++;
    } else {
      nextSequenceId = 0; 
    }
  } else {
    Serial.println(F("\nFailed to read status."));
    nextSequenceId = 0; 
  }
}
  • 17行目~29行目 最後に使われたシーケンスIDを読み取っています。 さらに、直前のコマンドが実行中の場合は完了を待機しています。

実行結果

実行すると、例えば次のようになります。

Initializing SD card...OK
Wait for response 
  Process Succeeded

Read iSDIO Status Register
 == iSDIO Status Registers == 
 [0400h] Command Write Status: 
... (snip) ...
 [0440h] Command Response Status #1: id = 3, sequence id = 1, status = Process Succeeded
... (snip) ...
 [0506h] WLAN: No Scan, No WPS, Group Client, AP, Infrastructure, No Connection, 
... (snip) ...
 [0550h] IP Address: 192.168.0.1
... (snip) ...

0. Show status
1. Disconnect
2. Establish

Command? (next sequence id = 2)

起動後の状態と、コマンドオプションを表示します。 この例では、起動時にFlashAirがAPモードで起動したことを表しています。

コマンドは、Arduino IDEのシリアルターミナル上部にあるボックスに入力します。

1、Enterと入力してみましょう。

Disconnect command: 

00: 01010000180000000000000000000700
01: 0700000000000000

Wait for response 
  Command Processing.
  Process Succeeded

Success.

Read iSDIO Status Register
... (snip) ...
 [0440h] Command Response Status #1: id = 7, sequence id = 2, status = Process Succeeded
... (snip) ...
 [0506h] WLAN: No Scan, No WPS, Group Client, STA, Initial, No Connection, 
... (snip) ...
 [0550h] IP Address: 0.0.0.0
... (snip) ...

Command? (next sequence id = 3)

14行目が、DisconnectのコマンドID7、指定したシーケンスID、ステータスProcess Succeededになっていることから、 Disconnectコマンドが発行され、完了したことがわかります。 また、WLANステータスがSTAでIP Addressの0.0.0.0であることから、無線LANが切断されていると判断できます。

3行目~4行目は、コマンドデータです。うまく動かないときは内容を確認してみてください。

引き続き、APモードの起動を行ってみましょう。

シリアルターミナルのボックスに、2、Enterと入力します。

Establish command: 

00: 01010000380000000000000000000300
01: 0800000003000000080000007364696F
02: 74657374080000003132333435363738
03: 0100000006000000

Wait for response 
  Command Processing.....
  Process Succeeded

Success.

Read iSDIO Status Register
... (snip) ...
 [0440h] Command Response Status #1: id = 3, sequence id = 3, status = Process Succeeded
... (snip) ...
 [0506h] WLAN: No Scan, No WPS, Group Client, AP, Infrastructure, No Connection, 
 [0508h] SSID: sdiotest
 [0528h] Encryption Mode: WPA2-PSK and AES
 [0529h] Signal Strength: 0
 [052Ah] Channel: 11
 [0530h] MAC Address: E8E0B758A7FB
 [0540h] ID: 
 [0550h] IP Address: 192.168.0.1
 [0554h] Subnet Mask: 255.255.255.0
 [0558h] Default Gateway: 192.168.0.1
 [055Ch] Preferred DNS Server: 192.168.0.1
 [0560h] Alternate DNS Server: 0.0.0.0
... (snip) ...

EstablishのコマンドID3が実行されています。(16行目)

WLANステータスがAPおよびIP Addressが192.168.0.1となっていることから、 FlashAirがAPモードで立ち上がっていることが確認できます。

arduino_tutorial_04.zip (24KB)

本チュートリアルのサンプルコードはGPLv3および二条項BSDライセンスで提供されています。 詳細はダウンロードした各ファイルを参照してください。


ページトップへ