がじぇっとるねさす

ホビーとエレクトロニクスを繋げるサイト

がじぇるね工房

作品タイトル:GR-KURUMIの機能を使う Wi-Fi編

表示名:@chobichan

GR-KURUMIの機能を使う Wi-Fi編

コンセプト・作品説明
GR-KURUMIでWi-Fiを動かします
####

GR-KURUMIでもWi-Fi

GR-KURUMIにXBee wifiを接続します。
XBee wifiとのインタフェースはシリアルかSPIの何れかを選べますが、ここではシリアルを選びました。
GR-KURUMIのシリアルはSerialでもSerial1でもSerial2でも構いませんが、Serial1でやっています。

XBee wifiは外部のマイコン等と有線接続を行う時、データの交換方法としてデータをフレーミングして交換するAPIモードと、行わないトランスペアレントモードが選べますが、まずは取っ付き易いトランスペアレントモードでサンプルアプリケーションを作ってみます。

作るのは「Ethernet編」で作成したインターネットラジオのwifi版です。

※XBeeの詳細は、以下の書籍
http://www.cqpub.co.jp/hanbai/books/48/48241.htm
または雑誌トランジスタ技術
http://toragi.cqpub.co.jp/Portals/0/backnumber/2012/04/p144.pdf
またはメーカーサイトを参照してください。
http://www.digi.com/products/xbee-rf-solutions/modules/xbee-wi-fi

 

####

XBee Wi-Fiとの接続

音楽の再生には再びWalSoundを使用します。

XBee wifiとの接続は回路図の様になっていますが、SPIに関係する配線は使用しません。事実上使用するのはTXD1、RXD1、リセット信号のRSTnになります。

 

####

受信バッファのサイズを増やす

インターネット上のラジオ配信サーバーから送られて来る音楽データは、まばらであったり、まとめて来たりと安定しない事が想定できます。
この為GR-KURUMI標準の64byteの受信サイズでは取りこぼしを起こしかねないので、「シリアル編」で行った「バッファサイズの調整」をSerial1に行います。

 

####

トランスペアレントモードで動かすXBee Wi-Fiの設定

トランスペアレントモードで動かす為には事前に相手先の情報をXBee wifiに書き込んで置きます。
相手先情報と言うのは、TCPまたはUDPのプロトコルタイプ、相手先のIPアドレスとポート番号になります。

X-CTUを使ってこれら情報をXBee wifiに書き込みます。
その内容を示します。※濃い青が標準から変更した項目です。ここに注目してください。
IP:TCPを選択しています。
TM:クライアント動作した時のタイムアウト時間で32は10進で50です。つまり5000msでタイムアウトします。
DO:デバイスオプションは0にします。
NS:この内容はDHCPを行った時に自動的に取得しました。
DL:音楽サーバーのIPアドレスです。「Ethernet編」でPLSファイルから得た値です。
DE:8015は書き間違えていますが、音楽サーバーのポート番号で本来は16進の1F4Fを書きます。

 

 

BD:ボーレートは230400bpsとしています。
AP:色は薄いままですが、トランスペアレントモードが選択されています。

 

 

【プログラミングのポイント】

XBee wifiとシリアル接続を行いました。シリアルはEthernetと異なりキャラクタデバイスなのでデータのブロック単位の処理は行いません。
データを1byte受信したら、MP3デコーダーICに1byte送信する処理を行っています。

参考コードを掲載しておきます。

実際のところEthernetと異なり、XBee wifiはデータの受信速度があまり上がりません。
途切れが気にならないBit Rateは32kbps程度でした。
なにか改善できればイイのですけれどね。

しかし有線と違い、家中どこでもインターネットラジオが聴けるのはイイですね。

 

/* ------------------------------------------------------------------------ */
/* XBee wifiでトランスペアレントモードでInternet radio      */
/*                                             designed by hamayan          */
/*                                             Copyright(C) hamayan         */
/*                                             since 2004 - 2016            */
/* ------------------------------------------------------------------------ */
#include  
#include  
#include    //for vs1063a

//Ethernet編と同一で省略

#define  RSTn    4  //xbee reset
#define  xbee    Serial1

struct INTERNET_RADIO
{
const char *name;  //station name
const char *domain;  //server domain
unsigned short port;  //server port
const char *fn;
int rate;  //bit rate
//const char *type;  //mp3 or aac
} station[] =
{
//  {"TheJazzGroove.com","199.180.72.2",8015,"/",128},  //port=0x1f4f
//  {"SmoothJazz.com Global Radio","75.126.221.202",8000,"/",128},  //port=0x1f40
//  {"SmoothJazz.com Global Radio","173.244.199.250",80,"/",64},  //port=0x50
//  {"JAZZ.FM91","209.159.189.54",8002,"/",32},  //port=0x1f42
{"SmoothJazz.com Global Radio","173.244.199.251",80,"/",32},  //port=0x50
};
int stationNumber = 0;

//Ethernet編と同一で省略

void setup()
{
  Serial.begin(230400UL);
  Serial.println("XBee wifi test.");

  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV8);  //2mhz
  SPI.setDataMode(SPI_MODE0);
  vs1011eInit();
  setVolum( 0x2020 );  //上位と下位に分かれている。数字が大きくなると音が小さくなる。
  //SPI.setClockDivider(SPI_CLOCK_DIV2);  //8mhz

  xbee.begin(230400UL);
  pinMode( RSTn, OUTPUT );  //xbee wifi hardware reset
  digitalWrite( RSTn, LOW );
  delay( 10UL );
  digitalWrite( RSTn, HIGH );
  delay(15 * 1000UL);
  Serial.print("connect to ");
  Serial.println(station[stationNumber].domain);

  while( true )
  {
    Serial.print(station[stationNumber].name);
    Serial.println(" connected.");
    // Make a HTTP request:
    String str = "GET ";
    str += station[stationNumber].fn;
    str += " HTTP/1.1\r\n";
    str += "Host: ";
    str += station[stationNumber].domain;
    str += "\r\n";
    str += "Accept: */*\r\n";
    str += "User-Agent: GR-KURUMI\r\n";
    str += "Connection: Close\r\n\r\n";
    xbee.print( str );
    Serial.print( str );

    unsigned long lastTime = millis();
    #define  TIMEOUT  5000UL
    #define  RCV_BUFFER_LIMIT  2048
    int rcvSz;
    byte *mp3Data;
    while( true )
    {
    rcvSz = xbee.available();
      if(rcvSz > 0)
      {
        /*XDCSのアサート*/
        VS_XDCS_IS_0;
          while( rcvSz )
          {
            unsigned char c = xbee.read();
            rcvSz--;
            /*書き込めるまで待ちを入れる*/
            while( VS_DRQ == false ) rot_rdq();
            /*データの書き込み*/
            SPI.transfer( c );
          }
        /*XDCSのネゲート*/
        VS_XDCS_IS_1;

        lastTime = millis();
      }
      else
      {
        if( (millis() - lastTime) > TIMEOUT )
        {
          Serial.println("TIMEOUT!");
          if( ++stationNumber >= (sizeof(station) / sizeof(station[0])) )
          stationNumber = 0;
          break;
        }
      }
    }
  }
}

void loop()
{
}

//Ethernet編と同一で省略

 

####

APIモードで動かすXBee Wi-Fiの設定

トランスペアレントモードは相手先と1対1の通信を行います。色々とTCP/IPに関する設定を事前に決めてしまう為マイコンのプログラミングは最小になりますが、限定的であるがゆえに融通が利きません。
インターネット上には様々なサービスがあり、それらを利用したいとするなら、APIモードを利用する事をお勧めします。

今回はNTPサーバーに接続してインターネット時計を作ってみます。

X-CTUを使ってこれら情報をXBee wifiに書き込みます。
その内容を示します。※濃い青が標準から変更した項目です。ここに注目してください。
トランスペアレントモードと異なりAPIモードでは必要な事項はAPIフレームで設定を行いますので、変更項目は少なくなっています。

DO:デバイスオプションは0にします。

 

 

ネットワークの項目もDHCPで得た情報をそのまま使います。
相手先のIPアドレスとポート番号は標準値です。

 

 

ここで重要なのはAPの項目がAPIモードを選択している事です。

 

 

【プログラミングのポイント】

1.XBee wifiの現在の状態を取得

APIモードを使って真っ先に何をするかと言えば、XBee wifiの現在の状態を調べます。
実は先のプログラムでは言及しませんでしたがXBee wifiがRESETから起動する時やアクセスポイントに接続してネットワークに参加するまでに時間が掛かります。
先のプログラムではdelayによる時間待ちで大雑把にこの時間を処理していました。
XBee wifiは起動した時やネットワークに参加した時はAPIフレームで状態の変化を伝えてきます。またマイコン側からその状態をAPIフレームを使って調べる事ができます。

XBee wifiのマニュアルを読むとAPIの項目の「Modem status」がそれに該当します。

XBee wifiからの応答コードが
0は起動した事、
1はWatch Dog TimerによるRESETが発生した事、
2はネットワークに参加した事、
3はアクセスポイントとの接続が解除されてしまった事を示します。
一旦XBee wifiから2が送られて来る事を待ちます。※マイコン側から「AT command」を使って現在の状態を調べる事もできます。
いずれにせよXBee wifiが2の状態に居ない時はネットワークを使う事ができないので、状態の把握は常に必要です。

2.DNSを使ってドメイン名からグローバルIPアドレスを取得

時計なので現在時刻をどこからか知る必要がありますが、今回はインターネット上のNTPサーバーから時刻を得ます。
日本のNTPサーバーで有名処はNICT(情報通信研究機構)です。
NTPサーバーのドメイン名は ntp.nict.jpになります。

XBee wifiにはDNSによる名前解決の機能が無いので、ユーザーが自力でそれを解決します。
つまりNTPサーバーにアクセスする前にDNSで名前解決を行います。一般的にDHCP機能を使ってネットワーク情報を得られたならば、最初に使うべきDNSサーバーのアドレスも取得しています。NSの項目がそれでした。

APIの「AT command」を使用してXBee wifiから現在のNSの値を取得し、そのIPアドレスに対してDNSによる名前解決を依頼します。

3.時刻サーバーから現在時刻の取得

DNSでNTPサーバーのIPアドレスを取得したら、今度はNTPプロトコルを使ってサーバーから現在時刻を得ます。

NTPのプロトコルについて規定しているRFC1305やRFC2030を読むと以下のパケットの構造が書かれています。

 

 

本来はNTPプロトコルの4つの64bit固定小数点の時刻データ(こちらの時刻、送信時刻、サーバーの受信時刻、送信時刻)を使って計算を行い※高精度な時刻を得ますが、ちょっと横着をしてサーバーの送信時刻(送信タイムスタンプ)を現在時刻としています。

このパケット構造はNTPに対してサービスを要求する側、応答する側共に同じです。

こちらから適切なデータを入れたパケットを送信すれば、NTPサーバーから応答が得られます。

※4つの時刻から、こちらの内部処理の遅延時間、ネットワーク上を往復する遅延時間、サーバーの内部処理に掛かる時間を算出し、そこから本当の時刻を得る。
 

4.時刻表示

使用したのは秋月電子で販売されているキャラクタタイプのOLEDディスプレイです。
http://akizukidenshi.com/catalog/g/gP-08278/

コントラストが液晶に比べて遥かに高く、離れていても、昼でも夜でもとても視認性の良いディスプレイです。

マイコンとの接続はI2Cで行い、3.3V電源で動き、プログラムでコントラストの調整もできてしまう便利な表示器です。

ここにNTPを使って校正した内部時刻を表示します。

 

####

送受信バッファのサイズを増やす

DNSのサービスを依頼したりNTPサービスを依頼する時も、一回のパケットのサイズは最大512byte程度が必要になります。今回はXBee wifiと接続しているSerialはSerial2です。
HardwareSerial.cppを開いてこのSerialの送信、受信バッファサイズを増やしてデータの取りこぼしを防ぎます。

 

 

【実行結果】

コードサイズが大きいのでこのページにプログラムを掲載していません。

図が実行結果です。矢印のところに時刻が表示されています。
最初の矢印では起動直後の標準の日付“2016/1/1“を表示していますが、次の矢印では現在の日付“2016/1/31“となりました。またOLEDディスプレイに現在の日付と時刻を表示しています。

 

 

@chobichan

極一般のハードウエアエンジニア。たまに雑誌記事を書いています。
twitterアカウント:https://twitter.com/chobichan

フォローする

share

ページトップへ