Developer World Spresense
English 中文
目次

1. Audio チュートリアル

Audio ライブラリには、オーディオの再生、録音をはじめ様々なサンプルスケッチが用意されています。

Spresense がもつマルチコアの特徴を生かして、音声のエンコード・デコードといったコーデック処理は、ユーザーアプリケーションが動作するメインコアとは別のコア (以降、DSP と呼びます) で実行します。これにより、ユーザーアプリケーションでは特に複雑な制御を必要とせず、簡単に Audio 機能を使ったアプリケーションを開発することができます。

Audio チュートリアルでは、DSP ファイルのインストール方法や、各種サンプルスケッチの動かし方について解説しています。

1.1. DSP ファイルのインストール

サンプルを動作させる際に、再生・録音するフォーマットの形式に合わせて、DSP ファイルを事前にインストールしておく必要があります。ここでは DSP ファイルのインストール方法について示します。

1.1.1. DSP ファイルの種類

表 1. DSPの種類
DSPファイル名 DSP インストーラー 説明

MP3DEC

mp3_dec_installer

MP3 オーディオ再生で使用します

MP3ENC

mp3_enc_installer

MP3 オーディオ録音で使用します

WAVDEC

wav_dec_installer

WAV (PCM) オーディオ再生で使用します

SRC

src_installer

WAV(PCM) オーディオ録音で使用します

1.1.2. DSP インストール方法

DSP のインストール手順は以下の二通りあります。

  • DSP ファイルをダウンロードして microSD カードへ直接コピーする方法

  • DSP インストーラーのスケッチを用いる方法

microSD カードへインストールする場合は、どちらの方法を使用しても構いません。 DSP インストーラーを使用する場合、拡張ボードの microSD カード以外に、メインボード上の SPI-Flash へインストールすることも可能です。

DSP ファイルのインストール作業は、DSP ファイルの更新が無い限り、一回実行してインストール済みの状態であれば毎回作業する必要はありません。
1.1.2.1. microSD カードへ DSP ファイルをコピーしてインストールする方法
  1. こちらから最新の DSP ファイルをダウンロードします。

  2. FAT32 フォーマット済みの microSD カードに BIN ディレクトリを作成し、ダウンロードしたファイルをコピーします。

    arduino dsp copy
    図 1. DSPファイルのコピー
  3. Audio サンプルを動作させる際に、この microSD カードを拡張ボードに挿して動かしてください。

1.1.2.2. DSPインストーラー用いてインストールする方法

DSP インストーラーは、インストールする DSP ファイルごとに用意されています。

  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → dsp_installer から起動します。

    arduino dsp installer ja
    図 2. DSPインストーラーの起動
  2. ツール → シリアルポート で Spresense の COM ポートを選択して、書き込みを実行します。

  3. コンパイル&書き込み(Upload)の実行が終わったら、シリアルモニタを起動します。

  4. ボーレート 115200 bps を選択すると、下図に示すようにメッセージが表示されます。

  5. インストール先の番号を選択して、送信ボタンを押します。
    microSD カードへインストールする場合は、拡張ボード上の microSD カードスロットへ FAT32 フォーマット済みの microSD カードを挿入してください。

    arduino dsp installer monitor1 ja
    図 3. DSPインストール先の選択
  6. インストールが成功すれば下図に示すメッセージ表示されます。

    arduino dsp installer monitor2 ja
    図 4. DSPインストール実行
オーディオの再生・録音のアプリケーションプログラムにおいて、DSP ファイルがインストールされた場所を initPlayer() 関数、または initRecorder() 関数の引数で指定します。デフォルトは microSD カード(/mnt/sd0/BIN) が指定されています。SPI-Flash に変更する場合は、/mnt/spif/BIN へ書き換えてください。

1.2. ハイレベルインターフェース層

Spresense Audioライブラリは、 Audioライブラリの開発ガイド にあるように、レイヤ構造を持持っています。
ハイレベルインターフェース層では、抽象度の高い典型的な機能をシステム全体の整合を取りながら実現しています。

+ 以下に、このハイレベルインターフェース層を利用したサンプルを示します。

1.2.1. ビープ音を鳴らしてみる

1.2.1.1. 概要

このサンプルは、Audio ハードウェアの機能を使用してビープ音を出力します。

Audio ライブラリの setBeep() 関数で、指定した周波数の音を出力します。 Arduino の tone() 関数によく似た機能ですが、圧電ブザー等を用意する必要はありません。 Spresense 拡張ボードのヘッドホン端子から音を出力します。

本サンプルでは、DSP ファイルのインストールは特に必要ありません。

1.2.1.2. 動作環境
  • Spresense メイン&拡張ボード

  • 再生用ヘッドホン or スピーカー

拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。

1.2.1.3. 動作手順
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → beep を選択してサンプルスケッチを開きます。

    arduino audio examples beep
  2. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

    arduino audio examples beep2
  3. ドレミファソラシドの音階がヘッドホンから出力されます。

1.2.1.4. プログラム解説
  • setPlayerMode() 関数でプレイヤーモードに設定します。

    theAudio->setPlayerMode(device,          (1)
                            player0bufsize,  (2)
                            player1bufsize); (3)
    1 device : スピーカーヘッドホン (AS_SETPLAYER_OUTPUTDEVICE_SPHP) へ出力するか、I2S (AS_SETPLAYER_OUTPUTDEVICE_I2SOUTPUT) へ出力するか、出力先のデバイスを選択します。
    このサンプルでは AS_SETPLAYER_OUTPUTDEVICE_SPHP を使用します。
    I2S を使用する場合、I2S へ出力するには を参照してください。
    2 player0bufsize : プレイヤー0 のバッファサイズを指定します。
    3 player1bufsize : プレイヤー1 のバッファサイズを指定します。
    詳細は、プレイヤーバッファサイズについて を参照ください。
    ビープ音の出力のみであればバッファは使用しないので、このサンプルでは 0 を指定しています。
  • ビープ音の出力は、setBeep() 関数を使用します。

    theAudio->setBeep(enable,     (1)
                      volume,     (2)
                      frequency); (3)
    1 enable に 1 を設定すると Beep 音の出力を開始し、enable に 0 が設定されると出力を停止します。
    2 volume : 音量を変更することができます (-90 ~ 0 [dB])。
    3 frequency : 指定した周波数の音を出力します。

1.2.2. MP3 の音楽を再生する

1.2.2.1. 概要

microSD カードにある MP3 音楽ファイルを再生します。

1.2.2.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 再生用ヘッドホン or スピーカー

このサンプルでは、microSD カードとヘッドホンを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。

1.2.2.3. 動作手順
  1. MP3デコード用の DSP ファイル MP3DEC を microSD カードへインストールします (DSP ファイルのインストール 参照)。

  2. 再生したい MP3 ファイルを "Sound.mp3" というファイル名で microSD カードのルートディレクトリに置きます。

  3. microSD カードを拡張ボードの microSD カードスロットに挿入します。

  4. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → player を選択してサンプルスケッチを開きます。

    arduino audio examples player
  5. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  6. スケッチがアップロードされ実行されると、microSD カードに置いた MP3 ファイルがヘッドホンから再生されます。
    ファイルを最後まで再生したら停止します。

1.2.2.4. プログラム解説
  • setRenderingClockMode() 関数でクロックモードを設定します。

    theAudio->setRenderingClockMode(mode); (1)
    1 mode : ノーマル (AS_CLKMODE_NORMAL) かハイレゾ (AS_CLKMODE_HIRES) かクロックモードを選択します。
    MP3 再生の場合は、AS_CLKMODE_NORMAL を指定してください。
  • setPlayerMode() 関数でプレイヤーモードに設定します。

    theAudio->setPlayerMode(device,  (1)
                            sp_drv); (2)
    1 device : スピーカーヘッドホン (AS_SETPLAYER_OUTPUTDEVICE_SPHP) へ出力するか、I2S (AS_SETPLAYER_OUTPUTDEVICE_I2SOUTPUT) へ出力するか、出力先のデバイスを選択します。
    このサンプルでは AS_SETPLAYER_OUTPUTDEVICE_SPHP を使用します。
    I2S を使用する場合、I2S へ出力するには を参照してください。
    2 sp_drv : 出力ドライブを設定します。
    このサンプルでは、AS_SP_DRV_MODE_LINEOUT を指定します。
    詳細は、拡張ボードのスピーカー端子を使用する場合 を参照してください。
  • initPlayer() 関数でプレイヤーの初期化を行います。

    theAudio->initPlayer(id,         (1)
                         codec,      (2)
                         codec_path, (3)
                         fs,         (4)
                         channel);   (5)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 codec : コーデックタイプは MP3 (AS_CODECTYPE_MP3) を指定します。
    3 codec_path : DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
    4 fs : サンプリングレートを指定します。
    MP3 では、32kHz (AS_SAMPLINGRATE_32000), 44.1kHz (AS_SAMPLINGRATE_44100), 48kHz (AS_SAMPLINGRATE_48000) をサポートしています。 AS_SAMPLINGRATE_AUTO 指定するとサンプリングレートを自動で判定します。
    5 channel : モノラル (AS_CHANNEL_MONO) か ステレオ (AS_CHANNEL_STEREO) を指定します。
  • writeFrames() 関数で MP3 ファイルの内容をデコーダーへ書き込みます。

    theAudio->writeFrames(id,      (1)
                          myFile); (2)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 引数に指定する myFile により、再生ファイルを変更することできます。
    このサンプルでは "Sound.mp3" というファイルを使用します。
    デコードが途切れないように定期的にこの関数を実行してデコーダーへストリームを供給します。 ファイルの終わりまで書き込まれたときに関数の戻り値として AUDIOLIB_ECODE_FILEEND が返されます。
  • startPlayer() 関数でプレイヤーを開始します。

    theAudio->startPlayer(id); (1)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    writeFrames() 関数でデコーダーへ書き込みを行ってから呼び出してください。
  • stopPlayer() 関数でプレイヤーを停止します。

    theAudio->stopPlayer(id,    (1)
                         mode); (2)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 mode で即座に停止するか (AS_STOPPLAYER_NORMAL)、デコーダに供給済みのストリームが再生し終わるまで待ってから停止するか (AS_STOPPLAYER_ESEND) を選択できます。 mode の引数無しで呼び出した場合、AS_STOPPLAYER_NORMAL で動作します。
  • setVolume(volume) 関数でボリュームの調整を行います。

    theAudio->setVolume(volume); (1)
    1 volume : 音量を変更できます。(-1020 ~ 120)
    指定した値の 1/10 が実際の音量[dB]になります。例えば、15 を指定したときの音量は 1.5 [dB] です。
1.2.2.5. プレイヤーバッファサイズについて

microSD カードからの読み出しに使用するバッファサイズとして、各プレイヤーごとにデフォルトで 160kByte を確保しています。 このサイズは、ハイレゾのWAV音声の読み出しが可能なサイズになっており、48kHzのMP3ファイルなどを再生する場合は setPlayerMode 関数の引数で使用するバッファサイズを削減することができます。 目安としては、192kHzのハイレゾのWAVファイルを再生する場合はデフォルトの 160kByte を、 128kbps程度のMP3ファイルを再生する場合は 24kByte ぐらいに設定することをお勧めします。

サイズに関しては、あくまで目安です。microSD カードの性能、アプリケーションの処理量などに応じて、調整してください。 バッファを小さくしすぎると、microSD カードからの読み出し時にエラーが発生します。読み出しデータのアンダーフローが発生する場合は、バッファサイズを大きくしてください。
1.2.2.6. I2S へ出力するには

出力デバイスをI2Sに切り替えたい場合は、 setPlayerMode での出力デバイスを AS_SETPLAYER_OUTPUTDEVICE_I2SOUTPUT に切り替えてください。 I2Sの動作モードは、Slaveモード、I2Sフォーマットモード、48000Hz、Stereo のみ対応しています。

1.2.2.7. 拡張ボードのスピーカー端子を使用する場合

オーディオ出力先として拡張ボードのヘッドホン端子を用いたラインアウトの場合、Audio 信号のドライブ能力設定として、 setPlayerMode の 引数 sp_drvAS_SP_DRV_MODE_LINEOUT を指定します (デフォルト)。

拡張ボードのスピーカー端子から出力する場合は、引数 sp_drvAS_SP_DRV_MODE_4DRIVER を指定してください。

スピーカー端子を使用する場合はボードの改修が必要です。詳細は以下のハードウェガイドを参照してください。

1.2.3. WAV の音楽を再生する

1.2.3.1. 概要

microSD カードにある WAV 音楽ファイルを再生します。

1.2.3.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 再生用ヘッドホン or スピーカー

このサンプルでは、microSD カードとヘッドホンを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。

1.2.3.3. 動作手順
  1. WAV デコード用の DSP ファイル WAVDEC を microSD カードへインストールします (DSP ファイルのインストール 参照)。

  2. 再生したい WAV ファイルを "Sound.wav" というファイル名で microSD カードのルートディレクトリに置きます。

  3. microSD カードを拡張ボードの microSD カードスロットに挿入します。

  4. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → player_wav を選択してサンプルスケッチを開きます。

    arduino audio examples player wav
  5. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  6. スケッチがアップロードされ実行されると、microSD カードに置いた WAV ファイルがヘッドホンから再生されます。
    ファイルを最後まで再生したら停止します。

1.2.3.4. プログラム解説
  • setRenderingClockMode() 関数でクロックモードを設定します。

    theAudio->setRenderingClockMode(mode); (1)
    1 mode: ノーマル (AS_CLKMODE_NORMAL) かハイレゾ (AS_CLKMODE_HIRES) かクロックモードを選択します。
    このサンプルでは AS_CLKMODE_NORMAL を使用します。
    この関数は、setPlayerMode() を呼び出す前か、ReadyMode の状態でのみ受け付けます。
    ノーマルとハイレゾとを動的に切り替える場合は、setReadyMode() を呼び出し Ready 状態に遷移させてから AS_CLKMODE_NORMAL or AS_CLKMODE_HIRES を切り替えてください。
  • setPlayerMode() 関数でプレイヤーモードに設定します。

    theAudio->setPlayerMode(device,  (1)
                            sp_drv); (2)
    1 device : スピーカーヘッドホン (AS_SETPLAYER_OUTPUTDEVICE_SPHP) へ出力するか、I2S (AS_SETPLAYER_OUTPUTDEVICE_I2SOUTPUT) へ出力するか、出力先のデバイスを選択します。
    このサンプルでは AS_SETPLAYER_OUTPUTDEVICE_SPHP を使用します。
    I2S を使用する場合、I2S へ出力するには を参照してください。
    2 sp_drv : 出力ドライブを設定します。
    このサンプルでは、AS_SP_DRV_MODE_LINEOUT を指定します。
    詳細は、拡張ボードのスピーカー端子を使用する場合 を参照してください。
  • initPlayer() 関数でプレイヤーの初期化を行います。

    theAudio->initPlayer(id,         (1)
                         codec,      (2)
                         codec_path, (3)
                         fs,         (4)
                         channel);   (5)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 codec : コーデックタイプは WAV (AS_CODECTYPE_WAV) を指定します。
    3 codec_path : DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
    4 fs : サンプリングレートを指定します。
    5 channel : モノラル (AS_CHANNEL_MONO) か ステレオ (AS_CHANNEL_STEREO) を指定します。
    このサンプルでは、はじめに WAV フォーマットを解析して、サンプリングレートやチャンネル情報を取得しています。
  • writeFrames() 関数で WAV ファイルの内容をデコーダーへ書き込みます。

    theAudio->writeFrames(id,          (1)
                          data,        (2)
                          write_size);
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 microSD から読み出すファイルを変更することで、再生ファイルを変更することができます。
    このサンプルでは "Sound.wav" というファイルから読み出したバッファを指定しています。
    デコードが途切れないように定期的にこの関数を実行してデコーダーへストリームを供給します。
  • startPlayer() 関数でプレイヤーを開始します。

    theAudio->startPlayer(id); (1)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    writeFrames() 関数でデコーダーへ書き込みを行ってから呼び出してください。
  • stopPlayer() 関数でプレイヤーを停止します。

    theAudio->stopPlayer(id,    (1)
                         mode); (2)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 mode で即座に停止するか (AS_STOPPLAYER_NORMAL)、デコーダに供給済みのストリームが再生し終わるまで待ってから停止するか (AS_STOPPLAYER_ESEND) を選択できます。 mode の引数無しで呼び出した場合、AS_STOPPLAYER_NORMAL で動作します。
  • setVolume(volume) 関数でボリュームの調整を行います。

    theAudio->setVolume(volume); (1)
    1 volume : 音量を変更できます。(-1020 ~ 120)
    指定した値の 1/10 が実際の音量[dB]になります。例えば、15 を指定したときの音量は 1.5 [dB] です。

1.2.4. ハイレゾオーディオを再生する

1.2.4.1. 概要

microSD カードにある ハイレゾ音源の WAV 音楽ファイルを再生します。

1.2.4.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 再生用ヘッドホン or スピーカー

このサンプルでは、microSD カードとヘッドホンを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。

1.2.4.3. 動作手順
  1. WAV デコード用の DSP ファイル WAVDEC を microSD カードへインストールします (DSP ファイルのインストール 参照)。

  2. 再生したい WAV ファイルを "HiResSound.wav" というファイル名で microSD カードのルートディレクトリに置きます。

  3. microSD カードを拡張ボードの microSD カードスロットに挿入します。

  4. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → player_hires を選択してサンプルスケッチを起動します。

    arduino audio examples player hires
  5. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  6. スケッチがアップロードされ実行されると、microSD カードに置いたハイレゾ WAV ファイルがヘッドホンから再生されます。
    ファイルを最後まで再生したら停止します。

1.2.4.4. プログラム解説
  • setRenderingClockMode() 関数でクロックモードを設定します。

    theAudio->setRenderingClockMode(mode); (1)
    1 mode: ノーマル (AS_CLKMODE_NORMAL) かハイレゾ (AS_CLKMODE_HIRES) かクロックモードを選択します。
    このサンプルでは AS_CLKMODE_HIRES を使用します。
    この関数は、setPlayerMode() を呼び出す前、ReadyMode の状態でのみ受け付けます。
    ノーマルとハイレゾとを動的に切り替える場合は、setReadyMode() を呼び出し Ready 状態に遷移させてから AS_CLKMODE_NORMAL or AS_CLKMODE_HIRES を切り替えてください。
  • setPlayerMode() 関数でプレイヤーモードに設定します。

    theAudio->setPlayerMode(device,  (1)
                            sp_drv); (2)
    1 device : スピーカーヘッドホン (AS_SETPLAYER_OUTPUTDEVICE_SPHP) へ出力するか、I2S (AS_SETPLAYER_OUTPUTDEVICE_I2SOUTPUT) へ出力するか、出力先のデバイスを選択します。
    このサンプルでは AS_SETPLAYER_OUTPUTDEVICE_SPHP を使用します。
    I2S を使用する場合、I2S へ出力するには を参照してください。
    2 sp_drv : 出力ドライブを設定します。
    このサンプルでは、AS_SP_DRV_MODE_LINEOUT を指定します。
    詳細は、拡張ボードのスピーカー端子を使用する場合 を参照してください。
  • initPlayer() 関数でプレイヤーの初期化を行います。

    theAudio->initPlayer(id,         (1)
                         codec,      (2)
                         codec_path, (3)
                         fs,         (4)
                         bitlen,     (5)
                         channel);   (6)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 codec : コーデックタイプは WAV (AS_CODECTYPE_WAV) を指定します。
    3 codec_path : DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
    4 fs : サンプリングレートを指定します。
    このサンプルでは 96kHz (AS_SAMPLINGRATE_96000) を指定しています。
    5 bitlen : ビット長を指定します。
    このサンプルでは 24bit (AS_BITLENGTH_24) を指定しています。
    6 channel : モノラル (AS_CHANNEL_MONO) か ステレオ (AS_CHANNEL_STEREO) を指定します。
  • writeFrames() 関数で WAV ファイルから読み出した内容をデコーダーへ書き込みます。

    theAudio->writeFrames(id,      (1)
                          myFile); (2)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 引数に指定する myFile により、再生ファイルを変更することできます。
    このサンプルでは "HiResSound.wav" というファイルを使用します。
    デコードが途切れないように定期的にこの関数を実行してデコーダーへストリームを供給します。 ファイルの終わりまで書き込まれたときに関数の戻り値として AUDIOLIB_ECODE_FILEEND が返されます。
  • startPlayer() 関数でプレイヤーを開始します。

    theAudio->startPlayer(id); (1)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    writeFrames() 関数でデコーダーへ書き込みを行ってから呼び出してください。
  • stopPlayer() 関数でプレイヤーを停止します。

    theAudio->stopPlayer(id,    (1)
                         mode); (2)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 mode で即座に停止するか (AS_STOPPLAYER_NORMAL)、デコーダに供給済みのストリームが再生し終わるまで待ってから停止するか (AS_STOPPLAYER_ESEND) を選択できます。 mode の引数無しで呼び出した場合、AS_STOPPLAYER_NORMAL で動作します。
  • setVolume(volume) 関数でボリュームの調整を行います。

    theAudio->setVolume(volume); (1)
    1 volume : 音量を変更できます。(-102 ~ 120 [dB])
    volume には 10 倍した整数値、例えば、1.5 [dB] の場合は 15 を指定します。

1.2.5. プレイリスト再生

1.2.5.1. 概要

このサンプルは、プレイリスト (playlist) を使用して音楽を再生するためのサンプルスケッチです。

microSD カードに入れた複数の音楽ファイルを順番に再生していきます。 再生、停止、次の曲へ送る、戻す、リピート再生やランダム再生といった操作を行うことができます。

1.2.5.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 再生用ヘッドホン or スピーカー

このサンプルでは、microSD カードとヘッドホンを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。

1.2.5.3. 動作手順

プレイリスト機能を使用するためには、PC 等で事前にプレイリストファイルを作成して microSD カードにコピーしておく必要があります。 本サンプルスケッチが使用する microSD カードのディレクトリ構成を以下に示します。

microSD カードルートディレクトリ
|-- BIN/
|   |-- MP3DEC
|   `-- WAVDEC
|-- AUDIO/
|   |-- Sound1.mp3
|   |-- Sound2.mp3
|   |--   :
|   |-- Sound1.wav
|   |-- Sound2.wav
|   |--   :
`-- PLAYLIST/
    `-- TRACK_DB.CSV
  • BIN/ ディレクトリに、MP3DECWAVDEC の DSP バイナリファイルをインストールします (DSP ファイルのインストール 参照)。

  • AUDIO/ ディレクトリに、プレイリストで再生する音楽コンテンツファイルをコピーします。

  • PLAYLIST/ ディレクトリに、プレイリストファイル ( TRACK_DB.CSV ) をコピーします。

プレイリストファイルの作成方法

プレイリストファイル TRACK_DB.CSV のフォーマットを以下に示します。 各ファイルの情報が行単位で書かれた CSV (comma-separated values) 形式のテキストファイルです。

[filename],[artist],[album],[channel number],[bit length],[sampling rate],[file format]
[filename],[artist],[album],[channel number],[bit length],[sampling rate],[file format]
[filename],[artist],[album],[channel number],[bit length],[sampling rate],[file format]
 :

音楽コンテンツのファイル名、アーティスト名、アルバム名や、コーデック情報(チャンネル数、ビット長、サンプリングレートHz)を記述してください。 TRACK_DB.CSV の例を以下に示します。

Sound1.mp3,Artist1,Album1,2,16,44100,mp3
Sound2.mp3,Artist1,Album1,2,16,44100,mp3
Sound1.wav,Artist2,Album2,2,16,48000,wav
Sound2.wav,Artist2,Album2,2,24,192000,wav

また、プレイリストファイルを作成するための簡単なスクリプトツール mkplaylist.py を提供しています。 こちら を右クリックしてファイルをダウンロードしてお使いください。 このスクリプトは ffmpeg-python を使用しているため、ffmpeg 及び ffmpeg-python をインストール済みの環境でご利用ください。

mkplaylist.py の使用方法を以下に示します。

python mkplaylist.py
usage: python mkplaylist.py dirname [dirname2] [dirname3] ...
usage: python mkplaylist.py -f filename

Generate an audio playlist file named as TRACK_DB.CSV

引数に音楽ファイルを含むディレクトリパスを指定して実行してください。 正常に実行を完了すると、TRACK_DB.CSV ファイルがカレントディレクトリに出力されます。 既に TRACK_DB.CSV ファイルが存在する場合はファイルの末尾に追加されます。

python mkplaylist.py MyMusic
  • 音楽コンテンツファイル名の拡張子は .mp3 もしくは .wav にしてください。

  • 日本語のような2バイトコードを含むファイル名はサポートされていません。ファイル名を ASCII コードの文字列に変更してください。

  • プレイリストファイルが CSV 形式で書かれているため、filename, artist, album 名に "," コンマを含む文字列を使用しないでください。

  1. microSD カードを拡張ボードの microSD カードスロットに挿入します。

  2. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → play_playlist を選択してサンプルスケッチを開きます。

    arduino audio examples player playlist
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  4. シリアルモニタを起動すると次のようなメニューが表示されます。シリアルからのキー入力にしたがって各種の動作を行います。

    === MENU (input key ?) ==============
    p: play  s: stop  +/-: volume up/down
    l: list  n: next  b: back
    r: repeat on/off  R: random on/off
    a: auto play      m,h,?: menu
    =====================================
EEPROM ライブラリを使用して、volume 値, random / repeat / auto play 等の設定情報を不揮発メモリに保存しています。 次回起動時に、前回プリセットされた情報をもとに動作するというサンプルアプリケーションになっています。 オリジナルのミュージックプレイヤーを作成する際に参考にしてください。

1.2.6. MP3 デュアルデコード再生

1.2.6.1. 概要

microSD カードにある 2 つの MP3 音楽ファイルを同時に再生します。

1.2.6.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 再生用ヘッドホン or スピーカー

このサンプルでは、microSD カードとヘッドホンを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。

1.2.6.3. 動作手順
  1. MP3デコード用の DSP ファイル MP3DEC を microSD カードへインストールします (DSP ファイルのインストール 参照)。

  2. 再生したい 2 つの MP3 ファイルを "Sound0.mp3", "Sound1.mp3" というファイル名で microSD カードのルートディレクトリに置きます。

  3. microSD カードを拡張ボードの microSD カードスロットに挿入します。

  4. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → dual_players を選択してサンプルスケッチを開きます。

    arduino audio examples dual players
  5. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  6. スケッチがアップロードされ実行されると、microSD カードに置いた 2 つの MP3 ファイルが同時にミックスされてヘッドホンから再生されます。それぞれのファイルを繰り返し再生します。

1.2.6.4. プログラム解説
  • setRenderingClockMode() 関数でクロックモードを設定します。

    theAudio->setRenderingClockMode(mode); (1)
    1 mode: ノーマル (AS_CLKMODE_NORMAL) かハイレゾ (AS_CLKMODE_HIRES) かクロックモードを選択します。
    MP3 再生の場合は、AS_CLKMODE_NORMAL を指定してください。
  • setPlayerMode() 関数でプレイヤーモードに設定します。

    theAudio->setPlayerMode(device,  (1)
                            sp_drv); (2)
    1 device : スピーカーヘッドホン (AS_SETPLAYER_OUTPUTDEVICE_SPHP) へ出力するか、I2S (AS_SETPLAYER_OUTPUTDEVICE_I2SOUTPUT) へ出力するか、出力先のデバイスを選択します。
    このサンプルでは AS_SETPLAYER_OUTPUTDEVICE_SPHP を使用します。
    I2S を使用する場合、I2S へ出力するには を参照してください。
    2 sp_drv : 出力ドライブを設定します。
    このサンプルでは、AS_SP_DRV_MODE_LINEOUT を指定します。
    詳細は、拡張ボードのスピーカー端子を使用する場合 を参照してください。
  • プレイヤー0, 1 それぞれに対して initPlayer() 関数でプレイヤーの初期化を行います。

    theAudio->initPlayer(id,         (1)
                         codec,      (2)
                         codec_path, (3)
                         fs,         (4)
                         channel);   (5)
    1 id : Player ID を選択します。
    一つは AudioClass::Player0 を指定し、もう一つは AudioClass::Player1 を指定します。
    2 codec : コーデックタイプは MP3 (AS_CODECTYPE_MP3) を指定します。
    3 codec_path : DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
    4 fs : サンプリングレートを指定します。
    MP3 では、32kHz (AS_SAMPLINGRATE_32000), 44.1kHz (AS_SAMPLINGRATE_44100), 48kHz (AS_SAMPLINGRATE_48000) をサポートしています。 AS_SAMPLINGRATE_AUTO 指定するとサンプリングレートを自動で判定します。
    5 channel : モノラル (AS_CHANNEL_MONO) か ステレオ (AS_CHANNEL_STEREO) を指定します。
  • プレイヤー0, 1 それぞれに対して writeFrames() 関数で MP3 ファイルの内容をデコーダーへ書き込みます。

    theAudio->writeFrames(id,      (1)
                          myFile); (2)
    1 id : Player ID を選択します。
    一つは AudioClass::Player0 を指定し、もう一つは AudioClass::Player1 を指定します。
    2 引数に指定する myFile により、再生ファイルを変更することできます。
    このサンプルでは "Sound0.mp3", "Sound1.mp3" というファイルを使用します。
    デコードが途切れないように定期的にこの関数を実行してデコーダーへストリームを供給します。 ファイルの終わりまで書き込まれたときに関数の戻り値として AUDIOLIB_ECODE_FILEEND が返されます。
  • プレイヤー0, 1 それぞれに対して startPlayer() 関数でプレイヤーを開始します。

    theAudio->startPlayer(id); (1)
    1 id : Player ID を選択します。
    一つは AudioClass::Player0 を指定し、もう一つは AudioClass::Player1 を指定します。
    writeFrames() 関数でデコーダーへ書き込みを行ってから呼び出してください。
  • プレイヤー0, 1 それぞれに対して stopPlayer() 関数でプレイヤーを停止します。

    theAudio->stopPlayer(id,    (1)
                         mode); (2)
    1 id : Player ID を選択します。
    一つは AudioClass::Player0 を指定し、もう一つは AudioClass::Player1 を指定します。
    2 mode で即座に停止するか (AS_STOPPLAYER_NORMAL)、デコーダに供給済みのストリームが再生し終わるまで待ってから停止するか (AS_STOPPLAYER_ESEND) を選択できます。 mode の引数無しで呼び出した場合、AS_STOPPLAYER_NORMAL で動作します。
  • setVolume(master, player0, player1) 関数でボリュームの調整を行います。

    theAudio->setVolume(master,   (1)
                        player0,  (2)
                        player1); (3)
    1 master : プレイヤー0,1 の両方に対する音量を変更できます。(-1020 ~ 120)
    2 player0 でプレイヤー0 に対する音量を変更できます。(-1020 ~ 120)
    3 player1 でプレイヤー1 に対する音量を変更できます。(-1020 ~ 120)
    指定した値の 1/10 が実際の音量[dB]になります。例えば、15 を指定したときの音量は 1.5 [dB] です。 master で全体の音量、player0, player1 で個別の音量を調整することができます。

1.2.7. マイクから入力された音を聞く

1.2.7.1. 概要

マイクから入力された音をヘッドホン端子から出力します。

Audio HWがもつパス設定機能を使用して、例えば、I2Sから入力されたデジタル音声をアナログに出力したり、 マイクからの入力をI2Sに出力するなど、省電力且つ非常にレイテンシの少ない出力機構を実現することができます。

1.2.7.2. 動作環境
  • Spresense メイン&拡張ボード

  • 再生用ヘッドホン or スピーカー

  • 録音用マイク

このサンプルでは、ヘッドホン、マイクを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。 マイクの接続については、以下のハードウェアガイドを参照してください。

本サンプルでは、DSP ファイルのインストールは特に必要ありません。

1.2.7.3. 動作手順
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → through を選択してサンプルスケッチを開きます。

    arduino audio examples through
  2. サンプルスケッチは I2S から入力された音声をヘッドホン端子に出力します。
    ここではスケッチを変更して、マイクから入力された音声をヘッドホン端子へ出力するように変更します。
    ファイル → 名前を付けて保存 して編集可能な場所へ保存します。

  3. setThroughMode() 関数の引数 inputAudioClass::I2sIn から AudioClass::MicIn へ変更して保存します。

  4. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  5. スケッチがアップロードされ実行されると、マイクから入力された音がヘッドホンから聞こえます。

1.2.7.4. プログラム解説
  • スルーモードの設定は、setThroughMode() 関数を使用します。

    theAudio->setThroughMode(input,      (1)
                             i2s_out,    (2)
                             sp_out,     (3)
                             input_gain, (4)
                             sp_drv);    (5)
    1 input で入力系統を選択します。
    2 i2s_out で I2S へ出力するソースを選択します。
    3 sp_out でヘッドホンスピーカーに出力するかどうかを選択します。
    例えば、マイクから入力された音声を I2S へ出力したい場合は、
    inputAudioClass::MicIn を指定、
    i2s_outAudioClass::Mic を指定、
    sp_outfalse を指定します。
    4 input_gain でマイクのゲインを設定できます。
    アナログマイクの場合は、0 ~ 21 [dB]、デジタルマイクの場合、-78.5 ~ 0 [dB] が設定できる範囲です。
    アナログマイクの場合は、input_gain に 10倍した整数値、例えば 10 [dB] を設定するときは 100 を指定します。
    デジタルマイクの場合は、input_gain に 100倍した整数値、例えば -0.05 [dB] を設定するときは 5 を指定します。
    5 sp_drv については、拡張ボードのスピーカー端子を使用する場合 を参照してください。

1.2.8. MP3 形式で録音する

1.2.8.1. 概要

マイクからの音声を録音し microSD カードに MP3 ファイルとして記録します。

1.2.8.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 録音用マイク

このサンプルでは、microSD カードとマイクを使用します。 マイクの接続については、以下のハードウェアガイドを参照してください。

1.2.8.3. 動作手順
  1. MP3 エンコード用の DSP ファイル MP3ENC を microSD カードへインストールします (DSP ファイルのインストール 参照)。

  2. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → recorder を選択してサンプルスケッチを開きます。

    arduino audio examples recorder
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  4. スケッチがアップロードされ実行されると、録音が開始され、一定時間で録音を停止します。

  5. 録音されたデータは "Sound.mp3" というファイル名で microSD カードに保存されています。
    microSD カードを取り出して、PC 上で録音されたデータを確認してみてください。

1.2.8.4. プログラム解説
  • setRecorderMode() 関数でレコーダーモードに設定します。

    theAudio->setRecorderMode(input_device, (1)
                              input_gain,   (2)
                              bufsize,      (3)
                              is_digital);  (4)
    1 input_device : 入力デバイスとしてマイク (AS_SETRECDR_STS_INPUTDEVICE_MIC) からの入力を指定します。
    2 input_gain : マイクのゲインを設定できます。
    アナログマイクの場合は、0 ~ 21 [dB]、デジタルマイクの場合、-78.5 ~ 0 [dB] が設定できる範囲です。
    アナログマイクの場合は、input_gain に 10倍した整数値、例えば 10 [dB] を設定するときは 100 を指定します。
    デジタルマイクの場合は、input_gain に 100倍した整数値、例えば -0.05 [dB] を設定するときは 5 を指定します。
    3 bufsize : レコーダーのバッファサイズを指定します。
    詳細は、_レコーダーバッファサイズについて を参照ください。
    4 接続しているマイクがデジタルマイクであった場合は引数 is_digitaltrue を指定します。
  • initRecorder() 関数でレコーダーの初期化を行います。

    theAudio->initRecorder(codec,      (1)
                           codec_path, (2)
                           fs,         (3)
                           channel);   (4)
    1 codec のコーデックタイプは MP3 (AS_CODECTYPE_MP3) を指定します。
    2 codec_path で DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
    3 fs でサンプリングレートを指定します。
    MP3 では、32kHz (AS_SAMPLINGRATE_32000), 44.1kHz (AS_SAMPLINGRATE_44100), 48kHz (AS_SAMPLINGRATE_48000) をサポートしています。
    4 channel : モノラル (AS_CHANNEL_MONO) か ステレオ (AS_CHANNEL_STEREO) を指定します。
  • readFrames() 関数でファイルへ記録するために MP3 エンコードされたデータを読み出します。

    theAudio->readFrames(myFile); (1)
    1 指定する myFile により、記録するファイルを変更できます。
    このサンプルでは "Sound.mp3" というファイルを使用します。
    エンコードバッファが溢れないように定期的にこの関数を実行してエンコード結果を読み出します。
  • startRecorder() 関数でレコーダーを開始します。

  • stopRecorder() 関数でレコーダーを停止します。

1.2.8.5. レコーダーバッファサイズについて

microSD カードへの書き込みに使用するバッファサイズとして、デフォルトで 160kByte を確保しています。 このサイズは、ハイレゾのWAV音声の書き込みが可能なサイズになっており、48kHzのMP3ファイルなどを再生する場合はメモリを余計に消費しています。 このため、48kHzのMP3ファイルを記録する場合は setRecorderMode で、使用するバッファサイズを変更することができます。 目安としては、192kHzのハイレゾのWAVファイルを再生する場合は、デフォルトの 160kByte を、 48kHzのMP3ファイルを記録する場合は、8kByte ぐらいに設定することをお勧めします。

サイズに関しては、あくまで目安です。microSD カードの性能、アプリケーションの処理量などに応じて、調整してください。 バッファを小さくしすぎると、microSD カードへの書き込み時にエラーが発生します。オーバーフローが発生する場合は、バッファサイズを大きくしてください。
音声取得ができたとしても、microSD カードの書き込み性能と動作しているアプリケーションなどの要因で、すべてのデータを書き込むことができない可能性があります。 現在の拡張ボードの場合、目安として、8ch/48kHz か、2ch/192kHzが限界になります。 また、microSD カードのスピードクラスにも依存するので、できる限り転送速度の速いものをお使いください。

1.2.9. WAV 形式で録音する

1.2.9.1. 概要

マイクからの音声を録音し microSD カードに WAV ファイルとして記録します。

1.2.9.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 録音用マイク

このサンプルでは、microSD カードとマイクを使用します。 マイクの接続については、以下のハードウェアガイドを参照してください。

1.2.9.3. 動作手順
  1. WAV(PCM) エンコード用の DSP ファイル SRC を microSD カードへインストールします (DSP ファイルのインストール 参照)。

  2. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → recorder_wav を選択してサンプルスケッチを開きます。

    arduino audio examples recorder wav
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  4. スケッチがアップロードされ実行されると、録音が開始され、一定時間で録音を停止します。

  5. 録音したデータは "Sound.wav" というファイル名で microSD カードに保存されています。
    microSD カードを取り出して、PC 上で録音されたデータを確認してみてください。

1.2.9.4. プログラム解説
  • setRecorderMode() 関数でレコーダーモードに設定します。

    theAudio->setRecorderMode(input_device, (1)
                              input_gain,   (2)
                              bufsize,      (3)
                              is_digital);  (4)
    1 input_device : 入力デバイスとしてマイク (AS_SETRECDR_STS_INPUTDEVICE_MIC) からの入力を指定します。
    2 input_gain : マイクのゲインを設定できます。
    アナログマイクの場合は、0 ~ 21 [dB]、デジタルマイクの場合、-78.5 ~ 0 [dB] が設定できる範囲です。
    アナログマイクの場合は、input_gain に 10倍した整数値、例えば 10 [dB] を設定するときは 100 を指定します。
    デジタルマイクの場合は、input_gain に 100倍した整数値、例えば -0.05 [dB] を設定するときは 5 を指定します。
    3 bufsize : レコーダーのバッファサイズを指定します。
    詳細は、_レコーダーバッファサイズについて を参照ください。
    4 接続しているマイクがデジタルマイクであった場合は引数 is_digitaltrue を指定します。
    ※ハイレゾ (192kHz) での記録を行う場合は、setRecorderMode() 関数を呼び出す前に、 setRenderingClockMode() 関数で AS_CLKMODE_HIRES を指定する必要があります。
  • initRecorder() 関数でレコーダーの初期化を行います。

    theAudio->initRecorder(codec,      (1)
                           codec_path, (2)
                           fs,         (3)
                           channel);   (4)
    1 codec のコーデックタイプは WAV (AS_CODECTYPE_WAV) を指定します。
    2 codec_path で DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
    3 fs でサンプリングレートを指定します。
    4 channel でチャンネル数を指定します。
    ※ハイレゾ (192kHz, 24bit) での記録を行う場合は、fsAS_SAMPLINGRATE_192000bitlenAS_BITLENGTH_24 を指定します。
  • WAVファイルを記録する場合、音声記録実行前に、writeWavHeader() 関数を呼び出してWAVヘッダを作成します。

    theAudio->writeWavHeader(myFile); (1)
    1 記録するファイルを指定します。
    このサンプルでは "Sound.wav" というファイルを使用します。
  • readFrames() 関数でファイルへ記録するために WAV(PCM) エンコードされたデータを読み出します。

    theAudio->readFrames(myFile); (1)
    1 指定する myFile により、記録するファイルを変更できます。
    このサンプルでは "Sound.wav" というファイルを使用します。
    エンコードバッファが溢れないように定期的にこの関数を実行してエンコード結果を読み出します。
  • startRecorder() 関数でレコーダーを開始します。

  • stopRecorder() 関数でレコーダーを停止します。

1.2.9.5. レコーディングが失敗する場合

microSD の状態、メーカなどによって、デフォルトのバッファサイズでも記録が失敗してしまうケースがあります。その場合は、書き込みに使用するバッファサイズを更に大きく確保してください。

setRecorderMode で、使用するバッファサイズを変更することができます。8チャンネル記録、ハイレゾステレオ記録などを行う場合、約500kBぐらいまでサイズを拡大するとかなり安定した記録動作になります。

ただし、その場合は、MainCore 用のメモリ領域が不足しますので、Arduino IDE 上から ツール → Memory → 1024KB を選択してメモリ領域を確保してください。

変更例.)

/* Select input device as microphone */
  theAudio->setRecorderMode(AS_SETRECDR_STS_INPUTDEVICE_MIC);

/* Select input device as microphone */
  theAudio->setRecorderMode(AS_SETRECDR_STS_INPUTDEVICE_MIC,,(500*1024));

1.2.10. PCM データを取り出す

1.2.10.1. 概要

マイクから入力された PCM データをキャプチャします。

このサンプルでは PCM データの先頭を表示しているだけですが、このサンプルを応用することで、PCM 生データの周波数解析処理や各種フィルタなどの信号処理を行うことができます。

1.2.10.2. 動作環境
  • Spresense メイン&拡張ボード

  • 録音用マイク

このサンプルでは、マイクを使用します。 マイクの接続については、以下のハードウェアガイドを参照してください。

本サンプルでは、DSP ファイルのインストールは特に必要ありません。

1.2.10.3. 動作手順
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → pcm_capture を選択してサンプルスケッチを開きます。

    arduino audio examples pcm capture
  2. サンプルスケッチはマイクから入力された音声を PCM データとして取り出します。

  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  4. シリアルモニタを起動すると、マイクから入力された音声の PCM データが表示されます。

1.2.10.4. プログラム解説
  • initRecorder() 関数でレコーダーの初期化を行います。

    theAudio->initRecorder(codec,      (1)
                           codec_path, (2)
                           fs,         (3)
                           channel);   (4)
    1 codec のコーデックタイプは PCM (AS_CODECTYPE_PCM) を指定します。
    2 codec_path で DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
    3 fs でサンプリングレートを指定します。
    48kHz (AS_SAMPLINGRATE_48000) の場合、DSP ファイルのインストールは不要になります。
    4 channel でチャンネル数を指定します。
    このサンプルでは、AS_CHANNEL_4CH を指定しています。
  • readFrames() 関数で PCM データを読み出します。

    theAudio->readFrames(p_buffer,    (1)
                         buffer_size, (2)
                         read_size);  (3)
    1 p_buffer へ PCM データを読み出します。
    2 読み出すサイズを指定します。
    3 実際に読み出されたサイズが返ってきます。
    キャプチャバッファが溢れないように定期的にこの関数を実行して PCM データを読み出します。
1.2.10.5. readFrames の読み出しサイズについて

readFrames で、読み出しバッファサイズを指定したとしても、内部で読み出し可能なデータがある場合、データのサイズ分だけ読み出します。内部の処理は 768 サンプルで行っており、バッファサイズを 2048 に指定しても 768 サイズのデータが存在する場合、768 サンプルを読み出すことになります。

バッファサイズまでデータを読み出したい場合は、残りの分を書き込むまで readFrames を繰り返すか、データが揃うだけ十分に待ってください。

1.2.11. 録音と再生を繰り返す

1.2.11.1. 概要

一定期間マイクから入力した音声をヘッドホンから再生する、という動作を繰り返し行います。

1.2.11.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 再生用ヘッドホン or スピーカー

  • 録音用マイク

このサンプルでは、microSD カードとヘッドホンを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。 マイクの接続については、以下のハードウェアガイドを参照してください。

1.2.11.3. 動作手順
  1. MP3デコード用の DSP ファイル MP3ENC, MP3DEC を microSD カードへインストールします (DSP ファイルのインストール 参照)。

  2. microSD カードを拡張ボードの microSD カードスロットに挿入します。

  3. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → rec_play を選択してサンプルスケッチを開きます。

    arduino audio examples rec play
  4. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  5. スケッチがアップロードされ実行されると、約 20 秒の周期で、マイクからの音声を記録し、録音されたデータを再生します。
    録音と再生を 5 回繰り返した後に終了します。

1.2.11.4. プログラム解説
  • 録音や再生機能については、他の単体サンプルと同様です。

  • setRecorderMode(), setReadyMode(), setPlayerMode() 関数を呼び出してモードを切り替えることで、動的に録音と再生動作の切り替えが可能になります。

1.3. オブジェクトインターフェース層

Spresense Audioライブラリは、 Audioライブラリの開発ガイド にあるように、レイヤ構造を持っています。
オブジェクトインターフェース層では、ハイレベルインターフェース層で実現できないような機能の組み合わせを実現できます。

以下に、このオブジェクトインターフェース層のサンプルを示します。

1.3.1. MP3 の音楽を再生する

1.3.1.1. 概要

microSD カードにある MP3 音楽ファイルを再生するサンプルです。 本サンプルはハイレベルインターフェースの MP3の音楽を再生する と機能としては同じサンプルになります。

1.3.1.2. 動作環境 及び 動作手順

スケッチは、Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → player_objif を選択することで可能です。

+ 動作環境 及び 動作手順 の詳細は、MP3の音楽を再生する をご覧ください。

1.3.1.3. プログラム解説
  • createStaticPools() 共有メモリのレイアウトを設定します。

      initMemoryPools();
      createStaticPools(no); (1)
    1 no : レイアウト番号を指定します。本サンプルでは、再生機能用レイアウト (MEM_LAYOUT_PLAYER) を指定してください。
  • 各Audioライブラリの生成と初期化します。

    theMixer->begin();                              (1)
    thePlayer->begin();                             (2)
    thePlayer->create(                              (3)
                        id,                         (4)
                        attention_cb);              (5)
    theMixer->create(                               (6)
                        attention_cb);              (5)
    thePlayer->activate(                            (7)
                        id,                         (4)
                        mediaplayer_done_callback); (8)
    theMixer->activate(                             (9)
                        OutputMixer0,               (10)
                        HPOutputDevice,             (11)
                        outputmixer_done_callback); (12)
    1 theMixer→begin
    MixerのAudio HWブロックの初期化及び電源投入などを行います。
    ※以前の theMixer→activateBaseband です。
    2 thePlayer→begin
    Playerの初期化を行います。
    ※Compatibility維持のため呼ばなくても動作します。
    3 thePlayer→create
    Playerのソフトウェアモジュールの生成・初期化をおこないます。
    4 id : PlayerのIDを指定します。Spresense では、再生は2本まで同時に行えます。MediaPlayer::Player0MediaPlayer::Player1 が指定できます。
    5 attention_cb : 各モジュールから通知されるエラー・ワーニング用のコールバックです。エラー処理を実装してください。
    6 theMixer→create
    Mixerのソフトウェアモジュールの生成・初期化をおこないます。
    7 thePlayer→activate
    Playerの再生動作モードの設定を行います
    8 mediaplayer_done_callback : 各モジュールから通知されるエラー・ワーニング用のコールバックです。エラー処理を実装してください。
    9 theMixer→activate
    Mixerの発音動作モードの設定を行います
    10 OutputMixer0 : MixerのIDを指定します。Playerと同じIDにしてください。
    11 HPOutputDevice : 出力先の設定を行います。 HPOutputDevice はアナログ出力になります。I2SOutputDevice はI2S出力になります。
    12 outputmixer_done_callback : Mixerにイベントを発行した際の結果通知用コールバックです。
  • setRenderingClockMode() 関数で発音のクロックモードを設定します。

    theMixer->setRenderingClkMode(mode); (1)
    1 mode : ノーマル (OUTPUTMIXER_RNDCLK_NORMAL) かハイレゾ (OUTPUTMIXER_RNDCLK_HIRES) かクロックモードを選択します。
    MP3 再生の場合は、OUTPUTMIXER_RNDCLK_NORMAL を指定してください。
  • Playerライブラリの初期化します。

    thePlayer->initPlayer(id,        (1)
                         codec,      (2)
                         codec_path, (3)
                         fs,         (4)
                         channel);   (5)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 codec : コーデックタイプは MP3 (AS_CODECTYPE_MP3) を指定します。
    3 codec_path : DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
    4 fs : サンプリングレートを指定します。
    MP3 では、32kHz (AS_SAMPLINGRATE_32000), 44.1kHz (AS_SAMPLINGRATE_44100), 48kHz (AS_SAMPLINGRATE_48000) をサポートしています。 AS_SAMPLINGRATE_AUTO 指定するとサンプリングレートを自動で判定します。
    5 channel : モノラル (AS_CHANNEL_MONO) か ステレオ (AS_CHANNEL_STEREO) を指定します。
  • writeFrames() 関数で MP3 ファイルの内容をデコーダーへ書き込みます。

    thePlayer->writeFrames(id,     (1)
                          myFile); (2)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 引数に指定する myFile により、再生ファイルを変更することできます。
    このサンプルでは "Sound.mp3" というファイルを使用します。
    デコードが途切れないように定期的にこの関数を実行してデコーダーへストリームを供給します。 ファイルの終わりまで書き込まれたときに関数の戻り値として AUDIOLIB_ECODE_FILEEND が返されます。
  • setVolume(volume) 関数でボリュームの調整を行います。

    thePlayer->setVolume(volume); (1)
    1 volume : 音量を変更できます。(-1020 ~ 120)
    指定した値の 1/10 が実際の音量[dB]になります。例えば、15 を指定したときの音量は 1.5 [dB] です。
  • startPlayer() 関数でプレイヤーを開始します。

    thePlayer->startPlayer(id,   (1)
                           mediaplayer_decode_callback (2)
                          );
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    writeFrames() 関数でデコーダーへ書き込みを行ってから呼び出してください。
    2 mediaplayer_decode_callback : Decodeフレーム単位で呼び出されるcallbackになります。 callback内で、Mixerへのデータ(AsPcmDataParam)の転送をする必要があります。下記のコードを実装してください。
    theMixer->sendData(OutputMixer0,
                       outmixer_send_callback,
                       pcm_param);
このcallbackで、Mixerへの転送前に出力すべきPCMをMixerへ送信側前に加工することが可能です。
  • stopPlayer() 関数でプレイヤーを停止します。

    thePlayer->stopPlayer(id,    (1)
                          mode); (2)
    1 id : Player ID を選択します。
    このサンプルでは AudioClass::Player0 を使用しています。
    2 mode で即座に停止するか (AS_STOPPLAYER_NORMAL)、デコーダに供給済みのストリームが再生し終わるまで待ってから停止するか (AS_STOPPLAYER_ESEND) を選択できます。 mode の引数無しで呼び出した場合、AS_STOPPLAYER_NORMAL で動作します。
1.3.1.4. プレイヤーバッファサイズについて

詳細は、MP3の音楽を再生する をご覧ください。

1.3.1.5. I2S へ出力するには

詳細は、MP3の音楽を再生する をご覧ください。

1.3.1.6. 拡張ボードのスピーカー端子を使用する場合

詳細は、MP3の音楽を再生する をご覧ください。

1.3.2. MP3 デュアルデコード再生

1.3.2.1. 概要

microSD カードにある 2 つの MP3 音楽ファイルを同時に再生するサンプルです。 本サンプルはハイレベルインターフェースの MP3デュアルデコード再生 と全く同じサンプルになります。

1.3.2.2. 動作環境 及び 動作手順

スケッチは、Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → dual_players_objif を選択することで可能です。

+ 動作環境 及び 動作手順 の詳細は、MP3デュアルデコード再生 をご覧ください。

1.3.2.3. プログラム解説

デュアルデコードは、MP3の音楽を再生する2つのplayer分を起動することになります。ただし、本サンプルは、MP3ファイルとWAVファイルを再生するサンプルになっており、それぞれのplayerは、playerごと独立してAudio ESデータを供給する必要があるため、互いの操作による影響を避けるために、playerごとにスレッドを作成しています。

それぞれの以下の詳細は、MP3の音楽を再生するを参照してください。

  • 共有メモリのレイアウトを設定します。

  • 各Audioライブラリの初期化します。

  • クロックモードを設定します。

  • Playerライブラリの初期化します。

  • MP3/WAVファイルの内容をデコーダーへ書き込みます。

  • プレイヤーを開始します。

  • プレイヤーを停止します。

クロックモードは、2つのプレイヤー共通の設定になります。レゾリューションの異なる発音はできません。
  • setVolume(master, player0, player1) 関数でボリュームの調整を行います。

    thePlayer->setVolume(master,   (1)
                         player0,  (2)
                         player1); (3)
    1 master : プレイヤー0,1 の両方に対する音量を変更できます。(-1020 ~ 120)
    2 player0 でプレイヤー0 に対する音量を変更できます。(-1020 ~ 120)
    3 player1 でプレイヤー1 に対する音量を変更できます。(-1020 ~ 120)
    指定した値の 1/10 が実際の音量[dB]になります。例えば、15 を指定したときの音量は 1.5 [dB] です。

    master で全体の音量、player0, player1 で個別の音量を調整することができます。

1.3.3. 低遅延再生する

1.3.3.1. 概要

スイッチのプッシュ音やゲーム、楽器などのように、何らかのinputに対して、高速に発音を行いたいケースに向けの、低遅延で再生のサンプルになります。 低遅延でmicroSD カードにある音声ファイル(RAWフィルのみ)を再生します。

低遅延再生をお試しいただくために、シェルで入力した文字に合わせて発音するようになっています。
また、低遅延を実現するために、音声ファイルの読み出しに時間がかかった場合、無音で補完し再生が止まらないようにしています。

1.3.3.2. 動作環境
  • Spresense メイン&拡張ボード

  • microSD カード

  • 再生用ヘッドホン or スピーカー

このサンプルでは、microSD カードとヘッドホンを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。

1.3.3.3. 動作手順
  1. 再生したい 音声PCMの RAWファイルを "sound0.raw"、"sound1.raw"、"sound2.raw" というファイル名で 3つのファイルを microSD カードのルートディレクトリに置きます。

    RAWファイルとは、WAVヘッダーなどのヘッダーのないPCMファイルです。ビット長16bitの場合、16ビットごとにチャンネルデータがインターリーブされた形になります。
    基本的にはSDカードからの読み出し速度が本サンプルの速度律速になりますので、現状は、以下にあるように16ビットのみ対応としています。
    やむを得ず、24ビットデータを使用したい場合、24ビットのRAWは32ビットごとにアライメントした形で下24ビットにデータを詰めて、チャンネルインタリーブした形で作成してください。
  2. microSD カードを拡張ボードの microSD カードスロットに挿入します。

  3. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → rendering_objif を選択してサンプルスケッチを開きます。

  4. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  5. スケッチがアップロードされ実行されると、シリアルモニタからの入力を待つ状態になります。この時に、"p\n"を入力すると"Sound0.raw"を再生、"o\n"を入力すると"Sound1.raw"を再生、"i\n"を入力すると"Sound2.raw"を再生します。"s\n"で再生を停止します。

NOTE:音声再生中に、新しい再生指示を受けると直ちに新しい再生が開始されます。

1.3.3.4. プログラム解説

SDカードの読み出し速度は、低遅延再生での速度ボトルネックになります。このため、SDカードからの読み出しをバッファリングするためにリングバッファを利用しています。 また、低遅延下での読み出し遅延に対するロバスト性を持たせるために、読み出しデータが欠落した際に無音を挿入することで、発音自体を止めないような作りになっています。

  • BridgeBuffer クラス

    SDカードから読み出したRAWデータをバッファリングするためのクラスです。
    writebuf(File& file, uint32_t size) でSDカード内の file オブジェクトで指定したファイルからバッファへ size 分だけデータを書き込みに行きます。
    readbuf(uint8_t *dst) で、 dst が示すアドレスへ、戻り値が示すサイズ分だけデータを読み出します。
    clearbuf() で、バッファをクリアします。

  • createStaticPools() 共有メモリのレイアウトを設定します。

      initMemoryPools();
      createStaticPools(no); (1)
    1 no : レイアウト番号を指定します。本サンプルでは、再生機能用レイアウト (MEM_LAYOUT_PLAYER) を指定してください。
  • Mixerライブラリの生成と初期化をします。

    theMixer->begin();                              (1)
    theMixer->create(                               (2)
                        attention_cb);              (3)
    theMixer->activate(                             (4)
                        OutputMixer0,               (5)
                        HPOutputDevice,             (6)
                        outputmixer_done_callback); (7)
    1 theMixer→begin
    MixerのAudio HWブロックの初期化及び電源投入などを行います。
    ※以前の theMixer→activateBaseband です。
    2 theMixer→create
    Mixerのソフトウェアモジュールの生成・初期化をおこないます。
    3 theMixer→activate
    Mixerの発音動作モードの設定を行います
    4 OutputMixer0 : MixerのIDを指定します。
    5 HPOutputDevice : 出力先の設定を行います。 HPOutputDevice はアナログ出力になります。 I2SOutputDevice はI2S出力になります。
    6 outputmixer_done_callback : Mixerにイベントを発行した際の結果通知用コールバックです。
  • setRenderingClockMode() 関数でクロックモードを設定します。

    theMixer->setRenderingClkMode(mode); (1)
    1 mode : ノーマル (OUTPUTMIXER_RNDCLK_NORMAL) かハイレゾ (OUTPUTMIXER_RNDCLK_HIRES) かクロックモードを選択します。
    低遅延再生の場合は、OUTPUTMIXER_RNDCLK_NORMAL を指定してください。
setRenderingClockMode() は、HWが起動される前、すなわち、begin() を呼び出す前に呼んでください。
  • setVolume(volume) 関数でボリュームの調整を行います。

    thePlayer->setVolume(volume); (1)
    1 volume : 音量を変更できます。(-1020 ~ 120)
    指定した値の 1/10 が実際の音量[dB]になります。例えば、15 を指定したときの音量は 1.5 [dB] です。
Mixerには、開始のための特殊なイベントを持ちません。Mixerにデータ(AsPcmDataParam)を3フレーム以上送信すると開始されます。
Mixerには、終了のための特殊なイベントを持ちません。Mixerに送信するデータ(AsPcmDataParam)の中の bool is_endture にして送信するとそのフレームの発音まで行い終了します。+ 終了時には該当フレームにFadeoutをかけています。
  • outmixer_send_callback 内の実装について outmixer_send_callback は1フレームの発音が終わると呼び出されます。この中で次の発音フレームを以下のコードでMixerに供給しなければなりません。

    theMixer->sendData(OutputMixer0,
                       outmixer_send_callback,
                       pcm_param);

    この際、データ(AsPcmDataParam pcm_param)の各パラメータにデータを代入する必要があります。

    static bool getFrame(AsPcmDataParam *pcm) {

    本サンプルでは、上記の関数内で代入処理をしています。データ構造の詳細は、SDK開発ドキュメントのAPI仕様を参照してください。

    また、本処理で、もし、データが読み出せなかった際に0で埋め発音を継続する処理が入っています。

        if (!getFrame(&pcm_param)) {    : Fifo
          break;
        }
    
        /* Send PCM */
        pcm_param.is_end = false;
        pcm_param.is_valid = true;
        if (pcm_param.size == 0) {      :0
          pcm_param.size = READSIZE;
          pcm_param.sample = pcm_param.size / BYTEWIDTH / CHNUM;
          memset(pcm_param.mh.getPa(), 0, pcm_param.size);  : 0
        }
1.3.3.5. フレームサイズとバッファサイズについて

本サンプルでは、フレームサイズを READSAMPLE (240) としています。これより小さい場合、オーディオシステム内の処理が間に合わなくなる可能性があります。 また、大きくすると遅延量が大きくなります。
バッファサイズは、BridgeBuffer 内の m_buf[30 * READSIZE]; で30フレーム分としています。適宜調整して下さい。

遅延を減らすため内部での信号処理を行わずに再生を行うため、チャンネルはStereo、サンプリングレートは48kHzのみになります。

1.3.4. MP3 形式で録音する

1.3.4.1. 概要

マイクからの音声を録音し microSD カードに MP3 ファイルとして記録します。 本サンプルはハイレベルインターフェースの MP3形式で録音する と機能としては同じサンプルになります。

1.3.4.2. 動作環境 及び 動作手順

スケッチは、Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → recorder_objif を選択することで可能です。

動作環境 及び 動作手順 の詳細は、MP3形式で録音する をご覧ください。

1.3.4.3. プログラム解説
  • createStaticPools() 共有メモリのレイアウトを設定します。

      initMemoryPools();
      createStaticPools(no); (1)
    1 no : レイアウト番号を指定します。本サンプルでは、録音機能用レイアウト (MEM_LAYOUT_RECORDER) を指定してください。
  • Recoderライブラリの生成と初期化します。

    theRecorder->begin(                         (1)
                       attention_cb);           (2)
    theRecorder->activate(                      (3)
                          dev,                  (4)
                          done_callback);       (5)
    1 theRecorder→begin
    Recorderのソフトウェアモジュールの生成をおこないます。
    2 attention_cb : 各モジュールから通知されるエラー・ワーニング用のコールバックです。エラー処理を実装してください。
    3 theRecorder→activate
    Recorderの記録動作モードの設定をおこないます。
    4 dev : 入力デバイスを設定します。マイクデバイスのみ対応です。AS_SETRECDR_STS_INPUTDEVICE_MIC を設定してください。
    5 done_callback : Recorderにイベントを発行した際の結果通知用コールバックです。
  • setCapturingClkMode() 関数で録音のクロックモードを設定します。

    theRecorder->setCapturingClkMode(mode); (1)
    1 mode : ノーマル (MEDIARECORDER_CAPCLK_NORMAL) かハイレゾ (MEDIARECORDER_CAPCLK_HIRES) かクロックモードを選択します。
    MP3 記録の場合は、MEDIARECORDER_CAPCLK_NORMAL を指定してください。
  • initRecorder() 関数でレコーダーの初期化を行います。

    theRecorder->init(codec,      (1)
                      channel,    (2)
                      fs,         (3)
                      bits,       (4)
                      bitrate,    (5)
                      path);      (6)
    1 codec のコーデックタイプは MP3 (AS_CODECTYPE_MP3) を指定します。
    2 channel : チャンネル数を指定します。1 / 2 が指定できます。
    3 fs : サンプリングレートを指定します。
    MP3 では、48kHz (48000) をサポートしています。
    4 bits : 音声データのビット長を指定します。MP3では、 16 を指定してください。
    5 bitrare : 圧縮によって生成されるESデータのビットレートを指定します。ここでは、 96000 にしています。
    6 path で DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
  • readFrames() 関数でファイルへ記録するために MP3 エンコードされたデータを読み出します。

    theRecorder->readFrames(ptr,          (1)
                            buffer_size,  (2)
                            size);        (3)
    1 ptr : 生成された音声ストリームを書き出すためのバッファの先頭アドレスを指定します。
    2 buffer_size : 生成された音声ストリームを書き出すためのバッファのサイズを指定します。
    3 size : 実際に書き出されデータのサイズが格納されます。
    sizeが0の場合は、書き出されるべきデータがありません。
  • start() 関数でレコーダーを開始します。

    theRecorder->start();
  • stop() 関数でレコーダーを停止します。

    theRecorder->stop();
1.3.4.4. レコーダーバッファサイズについて

_レコーダーバッファサイズについて を参照してください。

1.3.4.5. 書き込み時の信号処理について

書き込み時に、 signal_process(uint32_t size) を呼び出し、書き込みデータを加工することが可能な様にしていますが、 MP3の場合はデータを加工しないでください。

1.3.5. WAV 形式で録音する

1.3.5.1. 概要

マイクからの音声を録音し microSD カードに WAV ファイルとして記録します。 本サンプルはハイレベルインターフェースの WAV_形式で録音する と機能としては同じサンプルになります。

1.3.5.2. 動作環境 及び 動作手順

スケッチは、Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → recorder_wav_objif を選択することで可能です。

+ 動作環境 及び 動作手順 の詳細は、WAV_形式で録音する をご覧ください。

1.3.5.3. プログラム解説
  • createStaticPools() 共有メモリのレイアウトを設定します。

      initMemoryPools();
      createStaticPools(no); (1)
    1 no : レイアウト番号を指定します。本サンプルでは、録音機能用レイアウト (MEM_LAYOUT_RECORDER) を指定してください。
  • Recoderライブラリの生成と初期化します。

    theRecorder->begin(                         (1)
                       attention_cb);           (2)
    theRecorder->activate(                      (3)
                          dev,                  (4)
                          done_callback);       (5)
    1 theRecorder→begin
    Recorderのソフトウェアモジュールの生成をおこないます。
    2 attention_cb : 各モジュールから通知されるエラー・ワーニング用のコールバックです。エラー処理を実装してください。
    3 theRecorder→activate
    Recorderの記録動作モードの設定をおこないます。
    4 dev : 入力デバイスを設定します。マイクデバイスのみ対応です。AS_SETRECDR_STS_INPUTDEVICE_MIC を設定してください。
    5 done_callback : Recorderにイベントを発行した際の結果通知用コールバックです。
  • setCapturingClkMode() 関数で録音のクロックモードを設定します。

    theRecorder->setCapturingClkMode(mode); (1)
    1 mode : ノーマル (MEDIARECORDER_CAPCLK_NORMAL) かハイレゾ (MEDIARECORDER_CAPCLK_HIRES) かクロックモードを選択します。
    48kHzか16kHzでの記録の場合 MEDIARECORDER_CAPCLK_NORMAL を指定、192kHz記録の場合 MEDIARECORDER_CAPCLK_HIRES を指定してください。
  • initRecorder() 関数でレコーダーの初期化を行います。

    theRecorder->init(codec,      (1)
                      channel,    (2)
                      fs,         (3)
                      bits,       (4)
                      bitrate,    (5)
                      path);      (6)
    1 codec のコーデックタイプは WAV (AS_CODECTYPE_WAV) を指定します。
    2 channel : チャンネル数を指定します。1 / 2 / 4 / 8 が指定できます。
    3 fs : サンプリングレートを指定します。
    WAV では、16kHz (16000)、 48kHz (48000)、 192kHz (192000) をサポートしています。
    4 bits : 音声データのビット長を指定します。 16 か、 24 を指定してください。
    5 bitrare : WAVの場合、非圧縮(PCM)のみ対応なのでビットレートはDon’t careです。
    6 path で DSP ファイルがインストールされている場所を指定します。
    microSD カードの場合は "/mnt/sd0/BIN" を、SPI-Flash の場合は "/mnt/spif/BIN" を指定します。
  • readFrames() 関数でファイルへ記録するために PCMデータを読み出します。

    theRecorder->readFrames(ptr,          (1)
                            buffer_size,  (2)
                            size);        (3)
    1 ptr : PCMデータを書き出すためのバッファの先頭アドレスを指定します。
    2 buffer_size : 生成された音声データを書き出すためのバッファのサイズを指定します。
    3 size : 実際に書き出されデータのサイズが格納されます。
    sizeが0の場合は、書き出されるべきデータがありません。
  • start() 関数でレコーダーを開始します。

    theRecorder->start();
  • stop() 関数でレコーダーを停止します。

    theRecorder->stop();
1.3.5.4. レコーダーバッファサイズについて

_レコーダーバッファサイズについて を参照してください。

1.3.5.5. レコーディングが失敗する場合

レコーディングが失敗する場合 を参照してください。

1.3.5.6. readFrames の読み出しサイズについて

readFrames の読み出しサイズについて を参照してください。

1.3.5.7. 書き込み時の信号処理について

書き込み時に、signal_process(uint32_t size) を呼び出すことで、書き込みデータを加工することが可能です。 デジタルマイクでの録音時にGainをupしたいなど、必要な供給に合わせて信号処理を実装してください。

1.3.6. PCM データを取り出す

1.3.6.1. 概要

マイクから入力された PCM データをキャプチャします。 このサンプルでは PCM データの先頭を表示しているだけですが、このサンプルを応用することで、PCM 生データの周波数解析処理や各種フィルタなどの信号処理を行うことができます。 本サンプルはハイレベルインターフェースの PCMデータを取り出す と機能としては同じサンプルになります。

1.3.6.2. 動作環境 及び 動作手順

スケッチは、Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Audio → application → pcm_capture_objif を選択することで可能です。

+ 動作環境 及び 動作手順 の詳細は、PCMデータを取り出す をご覧ください。

実際に、アプリケーションとして本サンプルを使用する場合、signal_process() を所望の処理に合わせて各自実装していただくことで、 所望の集音した音声を信号解析などを行うアプリケーションとして使うことができます。 本サンプルでは、先頭8データの標準出力を行っていますが、こちらを変更してご使用ください。

1.3.6.3. プログラム解説
  • プログラムコードの先頭で、本サンプルコードの動作モードを指定しています。

    static const int32_t channel_num  = AS_CHANNEL_4CH;                                     (1)
    static const int32_t bit_length   = AS_BITLENGTH_16;                                    (2)
    static const int32_t frame_sample = 384;                                                (3)
    static const int32_t frame_size   = frame_sample * (bit_length / 8) * channel_num;      (4)
    
    static CMN_SimpleFifoHandle simple_fifo_handle;                                         (5)
    static const int32_t fifo_size  = frame_size * 10;                                      (6)
    static uint8_t fifo_buffer[fifo_size];                                                  (7)
    
    
    static const int32_t proc_size  = frame_size * 2;                                       (8)
    static uint8_t proc_buffer[proc_size];                                                  (9)
    1 チャンネル数を指定しています。ここでは4チャンネルの集音動作を指定しています。1 / 2 / 4 / 8 チャンネルが指定できます。
    2 音声データのビット長を指定します。 ここではビット長16ビットを指定しています。16 / 24 が指定できます。
    3 1フレームのサンプル数を指定します。 ここでは320サンプルを指定しています。240から1024までの数を指定してください。
    4 1フレームのバイト数を計算しています。
    5 データ読み出し用のFIFOのハンドル(CMN_SimpleFifoHandle)を定義しています。
    6 データ読み出し用のFIFOのサイズを指定します。ここでは、10フレーム分のサイズを指定しています。アプリケーションに合わせて調整してください。
    7 データ読み出し用のFIFOの領域を確保しています。
    8 信号処理用のメモリ領域のサイズを指定します。ここではフレームサイズの倍のサイズを指定しています。行いたい信号処理に合わせて指定してください。
    9 信号処理用のメモリ領域を確保してます。行いたい信号処理に合わせて取捨してください。
  • createStaticPools() 共有メモリのレイアウトを設定します。

      initMemoryPools();
      createStaticPools(no); (1)
    1 no : レイアウト番号を指定します。本サンプルでは、録音機能用レイアウト (MEM_LAYOUT_RECORDER) を指定してください。
  • FrontEndライブラリの生成と初期化します。

    theFrontEnd = FrontEnd::getInstance();      (1)
    
    theFrontEnd->begin(                         (2)
                       frontend_attention_cb);  (3)
    1 FrontEnd(集音用モジュール)のソフトウェアモジュールのインスタンス取得をおこないます。
    2 theFrontEnd→begin
    FrontEnd(集音用モジュール)のソフトウェアモジュールの初期化を行います。
    3 frontend_attention_cb : FrontEndモジュールから通知されるエラー・ワーニング用のコールバックです。エラー処理を実装してください。
  • setCapturingClkMode() 関数で録音のクロックモードを設定します。

    theFrontEnd->setCapturingClkMode(mode); (1)
    1 mode : ノーマル (FRONTEND_CAPCLK_NORMAL) かハイレゾ (FRONTEND_CAPCLK_HIRESO) かクロックモードを選択します。
    48kHzでの集音の場合 FRONTEND_CAPCLK_NORMAL を指定、192kHzでの集音の場合 FRONTEND_CAPCLK_HIRESO を指定してください。
  • activate() 関数で実際の集音動作をアクティブにします

    theFrontEnd->activate(frontend_done_callback); (1)
    1 frontend_done_callback : 各制御関数の完了イベントのためのコールバックを指定します。
  • init() 関数で集音動作の初期化を行います。

    theFrontEnd->init(channel_num,     (1)
                      bit_length,      (2)
                      frame_sample,    (3)
                      path,            (4)
                      dst);            (5)
    1 channel_num : チャンネル数を指定します。1 / 2 / 4 / 8 が指定できます。
    2 bit_length : 音声データのビット長を指定します。 AS_BITLENGTH_16 か、 AS_BITLENGTH_24 を指定してください。
    3 frame_sample : フレームのサンプル数を指定します。 240 から 1024 までの値を設定してください。+
    4 path : データの出力方法を指定します。アプリへコールバックによってデータを取得する場合は、AsDataPathCallback をしてしてください。
    5 dst : データの出力先を指定します。AsDataPathCallback を指定した場合、コールバック関数を指定してください。本サンプルでは、 dst.cb = frontend_pcm_callback; で指定しています。
  • start() 関数で集音動作を開始します。

    theFrontEnd->start();
  • stop() 関数で集音動作を停止します。

    theFrontEnd->stop();
  • frontend_pcm_callback() コールバック関数の中の処理は以下になっています。+

    static void frontend_pcm_callback(AsPcmDataParam pcm)
    {
      if (!pcm.is_valid) {                                                                              (1)
        puts("Invalid data !");
        return;
      }
    
      if (CMN_SimpleFifoGetVacantSize(&simple_fifo_handle) < pcm.size) {                                (2)
        puts("Simple FIFO is full !");
        return;
      }
    
      if (CMN_SimpleFifoOffer(&simple_fifo_handle, (const void*)(pcm.mh.getPa()), pcm.size) == 0) {     (3)
        puts("Simple FIFO is full !");
        return;
      }
    
      return;
    }
    1 取得されたデータが有効であるかをチェックします。有効でない場合はデータとして使わないでください。基本的には有効でないデータになることはありません。
    2 データ読み出し用のFIFOに空き領域があることをチェックします。
    3 データ読み出し用のFIFOに空き領域がある場合は、FIFOにデータを書き込みます。FrontEnd Object からは、pcm.mh.getPa() で示すアドレスに、 pcm.size サイズ分だけPCMデータが書き込まれています。
  • アプリケーションループでの信号処理について

    実際の信号処理は、 loop() のアプリケーションループ内で処理をします。 コールバックで書き込まれたデータ読み出し用のFIFOにあるデータを、ループ内で適宜読み出して、 execute_aframe でフレーム単位の処理を行います。

bool execute_aframe()
{
  size_t size = CMN_SimpleFifoGetOccupiedSize(&simple_fifo_handle);                  (1)

  if (size > 0) {
    if (size > proc_size) {
      size = (size_t)proc_size;
    }

    if (CMN_SimpleFifoPoll(&simple_fifo_handle, (void*)proc_buffer, size) == 0) {    (2)
      printf("ERROR: Fail to get data from simple FIFO.\n");
      return false;
    }

    signal_process(size);                                                            (3)

  }
1 データ読み出し用のFIFOにあるデータのサイズを取得します。
2 データ読み出し用のFIFOにから信号処理用のバッファにデータをコピーします。
3 信号処理などの所望の処理を行います。 signal_process の中身については、各アプリケーションごとに実装してください。
1.3.6.4. バッファサイズについて

PCMデータを取り出す をご覧ください。

1.3.6.5. 書き込み時の信号処理について

取得した音声データを signal_process(uint32_t size) の中で信号処理(周波数解析、AIなど)を行うことで 所望のセンシング処理が行えます。

2. GPS チュートリアル

このチュートリアルではサンプルスケッチをベースに Spresense GPS 機能を使った簡単なアプリケーションや、 サンプルをカスタマイズして機能を追加・拡張する方法について解説します。

2.1. GPS サンプルスケッチ

2.1.1. 概要

GNSS ライブラリの Examples にあるサンプルスケッチ gnss.ino をベースに GNSS ライブラリの基本機能について説明します。

2.1.2. 動作環境

本スケッチの動作に必要な環境を以下に示します。
メインボードに GPS チップアンテナが搭載されており、メインボードだけで GPS 機能を動かすことができます。

  • Spresense メインボード

2.1.3. 動作手順

Arduino IDE メニューから File → Examples → GNSS → gnss (ファイル → スケッチ例 → GNSS → gnss) スケッチを開きます。

gnss example1
gnss example2

このスケッチを Spresense ボードに書き込みます。

gnss example3

シリアルモニタを開くと、測位情報が表示されます。測位中は LED0 が 1 秒間隔で点滅します。

gnss example4
  • 時刻は初期時刻 1980/01/06 00:00:00 からカウントアップします。
    衛星から正確な時刻が取得されたら現在の UTC 時刻表示に切り替わります。

  • numSat: 見つかった衛星数を表します

  • 非測位状態のとき "No-Fix, No Position" と表示されます。
    測位ができたら "Fix" と Lat=緯度、Lon=経度を表示します。
    また、位置情報が取得できたら LED1 が点灯します。

gnss example5

1 分おきに衛星の詳細情報を表示します。

  • Type: 衛星種類、Id: 衛星番号、Elv: 仰角、Azm: 方位角、CN0: 信号強度を表します。

2.1.4. プログラム解説

  • setup() 関数で GNSS ライブラリの初期設定を行います。

    enum ParamSat {
      eSatGps,            /**< GPS                     World wide coverage  */
      eSatGlonass,        /**< GLONASS                 World wide coverage  */
      eSatGpsSbas,        /**< GPS+SBAS                North America        */
      eSatGpsGlonass,     /**< GPS+Glonass             World wide coverage  */
      eSatGpsBeidou,      /**< GPS+BeiDou              World wide coverage  */
      eSatGpsGalileo,     /**< GPS+Galileo             World wide coverage  */
      eSatGpsQz1c,        /**< GPS+QZSS_L1CA           East Asia & Oceania  */
      eSatGpsGlonassQz1c, /**< GPS+Glonass+QZSS_L1CA   East Asia & Oceania  */
      eSatGpsBeidouQz1c,  /**< GPS+BeiDou+QZSS_L1CA    East Asia & Oceania  */
      eSatGpsGalileoQz1c, /**< GPS+Galileo+QZSS_L1CA   East Asia & Oceania  */
      eSatGpsQz1cQz1S,    /**< GPS+QZSS_L1CA+QZSS_L1S  Japan                */
    };
    /* Set this parameter depending on your current region. */
    static enum ParamSat satType =  eSatGps; (3)
    
    void setup() {
       :
      /* Activate GNSS device */
      result = Gnss.begin();           (1)
    
      Gnss.select(XXX);                (2)
    
      /* Start positioning */
      result = Gnss.start(COLD_START); (4)
       :
    }
    1 Gnss.begin() 関数で初期化を行います。この関数を呼び出すと GNSS 専用ファームウェアをロードします。 正しく動作しない場合は、Spresense ブートローダーのインストール を参考に常に最新版のブートローダーをインストールしてください。
    2 Gnss.select() 関数で使用する衛星を選択します。
    3 衛星の選択で複数の衛星を組み合わせて使用することができます。
    例えば、QZSS みちびきを使用する場合、satTypeeSatGpsQz1cQz1S へ変更してください。
    GPS, QZSS に加えて、{Glonass, BeiDou, Galileo} の 3 種類からいずれか 1 つを選択して追加することができます。
    4 Gnss.start() 関数で測位を開始します。ここではコールドスタートを行います。
  • loop() 関数で GNSS 測位結果を周期的に取得します。

    void loop()
    {
       :
      /* Check update. */
      if (Gnss.waitUpdate(-1)) (1)
      {
        /* Get NaviData. */
        SpNavData NavData;
        Gnss.getNavData(&NavData); (2)
    1 Gnss.waitUpdate() 関数でGNSS からの受信を待ちます。
    2 Gnss.getNavData() 関数で測位結果を取得します。ここで得られた NavData をもとに print_pos() 関数や print_condition() 関数で測位情報を出力します。 詳細は、SpNavData を参照してください。
  • 機能の使用例として、本サンプルでは 5 分周期で GNSS 測位を停止/再開するという動作を行います。

      if (LoopCount >= RESTART_CYCLE)
      {
    
        /* Restart GNSS. */
        if (Gnss.stop() != 0)                (1)
        {
          Serial.println("Gnss stop error!!");
          error_flag = 1;
        }
        else if (Gnss.end() != 0)            (2)
        {
          Serial.println("Gnss end error!!");
          error_flag = 1;
        }
        else
        {
          Serial.println("Gnss stop OK.");
        }
    
        if (Gnss.begin() != 0)               (3)
        {
          Serial.println("Gnss begin error!!");
          error_flag = 1;
        }
        else if (Gnss.start(HOT_START) != 0) (4)
        {
          Serial.println("Gnss start error!!");
          error_flag = 1;
        }
        else
        {
          Serial.println("Gnss restart OK.");
        }
    1 Gnss.stop() 関数で測位を停止します。
    2 Gnss.end() 関数で終了処理を行います。この関数を呼び出すと GNSS 専用ファームウェアをアンロードします。
    3 Gnss.begin() 関数で再び初期化を行います。
    4 Gnss.start() 関数に HOT_START を指定します。電源を落とさずにホットスタートを行うことで、直前までの測位で取得された時刻情報、位置情報や衛星起動情報を利用して即座に位置情報を取得することができます。

2.2. GPS トラッカーを使ってみよう

2.2.1. 概要

GNSS ライブラリの Examples にある GPS トラッカーのサンプルスケッチ gnss_tracker を動かしてみます。
このスケッチは、いわゆる GPS データロガーと呼ばれるもので、 定期的に GPS から取得した位置情報を microSD カードに記録していきます。 microSD カードに保存されたファイルを後から取り出し、パソコン等の専用ツールを使うことによって、 移動の軌跡を地図上に重ねて閲覧表示することができます。

2.2.2. 動作環境

本スケッチの動作に必要な環境を以下に示します。

  • Spresense メインボード

  • Spresense 拡張ボード

  • microSD カード

2.2.3. 動作手順

Spresense のメインボードと拡張ボードを接続し、拡張ボードに microSD カードを挿入してください。
Arduino IDE メニューから File → Examples → GNSS → gnss_tracker (ファイル → スケッチ例 → GNSS → gnss_tracker) スケッチを開きます。

gnss tracker1
gnss tracker2

このスケッチを Spresense ボードに書き込みます。

gnss tracker3

シリアルモニタを開くと、はじめにアプリケーションの設定情報を出力した後に、測位結果の出力を開始します。

gnss tracker4

2.2.4. 動作仕様

電源が供給されるとまず初めに microSD カード内の tracker.ini ファイルに書かれた設定を読み込みます。 このファイルに書かれた設定にしたがってアプリケーションの初期設定をした後に、GPS 測位動作を開始します。 動作中は、メインボード上の LED によってアプリケーションの動作状態を表します。

  • LED 仕様

    アプリケーションの動作が開始するときに 4 つの全ての LED が一回だけ点滅します。
    測位停止中は 全ての LED が消灯します。個別の LED に関する動作仕様は以下の通りです。

    LED 説明

    LED0

    測位動作中に点灯/消灯を繰り返します

    LED1

    現在の位置情報が取得(FIX)できたら点灯します

    LED2

    ファイルアクセス中に点灯します

    LED3

    microSD カードにアクセスできないなど、何かしらのエラーが発生したときに点灯します

  • microSD カード内のファイル構成

    microSD カード内のファイル構成を以下に示します。ファイルが存在しない場合は、プログラム内でファイルを新規に作成するので、事前にファイルを用意しておく必要はありません。

    ファイル名 説明

    tracker.ini

    アプリケーションの設定情報が書かれたテキストファイルです。各設定値については後述します。このファイルが存在しないときは、デフォルトの設定値をもつファイルが新規に作成されます。

    index.ini

    インデックス番号が書かれたテキストファイルです。この番号は GPS データ出力先のファイル名を表します。例えば “00000018” と書かれている場合は、ファイル名 “00000018.txt” に GPS データを出力します。繰り返し動作させたときに既存のファイルを上書きしないように、この数字は 1 ずつインクリメントされていきます。ファイルが存在しない場合は、“00000001” と書かれた新規ファイルが作成されます。

    00000001.txt
    00000002.txt
      :

    GPS データが書き込まれるテキストファイルです。このファイルにはNMEA 0183 フォーマットの GPGGA センテンスが格納されます。 ユーザはこのファイルをパソコン等に取り込んで GPS トラッキングデータを生成します。

  • tracker.ini 設定情報

    ここで定義される設定パラメータを変更することでアプリケーションの動作を切り替えることができます。デフォルトのままでも動作するため、まず動かしてみるという場合はここを読み飛ばして頂いて構いません。

    変数 説明 設定例

    SatelliteSystem

    (GPS/GLONASS/SBAS/QZSS_L1CA/QZSS_L1S)
    衛星システム(の組み合わせ)を下記から選択します。
    GPS+GLONASS+QZSS_L1CA
    GPS+BEIDOU+QZSS_L1CA
    GPS+GALILEO+QZSS_L1CA
    GPS+QZSS_L1CA+QZSS_L1S
    GPS+QZSS_L1CA
    GPS+GLONASS
    GPS+BEIDOU
    GPS+GALILEO
    GLONASS
    GPS+SBAS
    GPS

    SatelliteSystem=GPS+GLONASS+QZSS_L1CA

    NmeaOutUart

    (TRUE/FALSE)
    NMEA 出力をシリアルモニタに表示するかどうかを選択します。

    NmeaOutUart=TRUE

    NmeaOutFile

    (TRUE/FALSE)
    NMEA 出力をファイルに保存するかどうかを選択します。ここは TRUE を指定してください。

    NmeaOutFile=TRUE

    BinaryOut

    (TRUE/FALSE)
    GNSSバイナリデータを保存するかどうかを選択します。通常、FALSE のままで構いません。

    BinaryOut=FALSE

    IntervalSec

    (1-300)
    測位間隔[秒]を設定します。

    IntervalSec=1

    ActiveSec

    (60-300)
    測位の有効期間[秒]を設定します。ActiveSec秒間だけ測位した後に、SleepSec秒間は測位を停止する、という動作を周期的に繰り返します。

    ActiveSec=60

    SleepSec

    (0-240)
    測位を停止する期間[秒]を設定します。ActiveSec秒間だけ測位した後に、SleepSec秒間は測位を停止する、という動作を周期的に繰り返します。Sleep 期間を設けず、常時測位し続ける場合は 0 を指定してください。

    SleepSec=240

    UartDebugMessage

    (NONE/ERROR/WARNING/INFO)
    シリアルモニタへ出力するデバッグレベルを設定します。

    UartDebugMessage=NONE

2.2.5. GPS ロガーデータの確認手順

GPS ロガーデータを確認する方法は様々ありますが、ここでは NMEA を KML 形式へ変換して、Google Earth を使って地図上に表示します。

  • Google Earth のインストール

    Google Earthのダウンロードページから Google Earth プロをダウンロードしてインストールしてください。

  • NMEA から KML/KMZ へ変換する

    microSD カード上に保存された 00000001.txt ~ 99999999.txt ファイルの中身は、NMEA フォーマット (GPGGA センテンスのみ) で書かれています。これを Google Earth で扱うことができる KML/KMZ 形式へ変換します。変換元のデータが複数のテキストファイルに分かれている場合は、ファイルを結合して一つのファイルにしてください。

    1. https://www.h-schmidt.net/NMEA/ を開く

    2. “ファイルを選択” に入力ファイルを指定して “送信” ボタンを押す

    3. KML に変換されたファイルをダウンロードすることができます

      gnss nmea2kml1
      gnss nmea2kml2
      他にも https://www.gpsvisualizer.com サイトでも Google Earth やその他のファイルフォーマットへ変換することができます。また、Webサイト上で変換するのではなく、NMEA to KMZ Utility など、PC 上で動作する変換ツールも世の中に各種あります。
  • KML ファイルを Google Earth で開く

    移動した軌跡が地図上にマッピングされ表示されます。

ここではサンプルコードをそのまま動作させる方法について説明しました。

以降の章で GPS トラッカーをカスタマイズして使用する応用編について解説していく予定です。

2.3. GPS トラッカーをメインボードで動かす

2.3.1. 概要

前章では、GPS トラッカーのサンプルスケッチ gnss_tracker.ino をそのまま動かしてみました。このサンプルは GPS ロガーデータを microSD カードに保存していましたが、これをメインボード上の Flash メモリに保存するように変更します。メインボードのみを使ってより小型で省電力な GPS トラッカーに改造してみます。

2.3.2. 動作環境

本スケッチの動作に必要な環境を以下に示します。

  • Spresense メインボード

2.3.3. 変更手順

gnss_tracker.ino のスケッチを開きます。

gnss tracker1
gnss tracker2

Arduino IDE メニューから File → Save as…​ (ファイル → 名前を付けて保存) を選択して、スケッチを任意のフォルダに保存します。サンプルスケッチがコピーされて編集が可能になります。

gnss tracker5

gnss_file.cpp を開いて、以下の変更を行います。

  • #include <Flash.h> 行の追加

  • SDClass theSD; の削除

  • “theSD” → “Flash” へ一括置換

    • Flash を使用するように変更します。Flash ライブラリは SD と同一のインターフェース構成になっているのでオブジェクト名を変更するだけで使用可能です。

    • 参考までに変更例を以下に示します。

      gnss tracker7

2.3.4. 動作手順

動作手順は基本的に SD の場合と同じです。さきほど編集したスケッチを Spresense ボードに書き込んで実行してください。

2.3.5. GPS ロガーデータの確認手順

Flash メモリに保存された結果を取り出す方法はいくつかあります。(ここで紹介する方法以外にも Add-on ボードなどを利用してネットワークを介してファイルを転送する方法もあります)

  • ファイルダンプする方法

    GPS ロガーデータはテキストファイルとして Flash 内に保存されています。中身をシリアルターミナルにダンプし、その表示結果をファイルとして保存します。 とても原始的な方法ですが、手っ取り早く簡単に行うことができます。

    Arduino IDE のシリアルモニタにはシリアル表示結果をファイルに保存する機能が無いため、TeraTerm や minicom といった別のターミナルを使用することを推奨します。
    1. nuttx_shell スケッチをダウンロードしてください。

      スケッチを書き込んで実行すると、シリアルターミナルに NuttShell nsh> プロンプトが表示されます。

    2. ls コマンドで Flash 内のファイルリストを確認します。

      nsh> ls -l /mnt/spif
      gnss tracker8
    3. 対象となるログファイルを cat コマンドで表示します。
      コマンド実行前にログ結果をファイルに保存するように設定しておいてください。

      nsh> cat /mnt/spif/00000003.txt
      gnss tracker9

      GPS ロガーデータを取り出すことができました。
      Flash 内のファイルが不要になれば rm コマンドで削除することができます。

      nsh> rm /mnt/spif/00000003.txt
  • Flash → microSD カードにコピーして取り出す方法

    拡張ボードをもっている場合は、拡張ボードを使って Flash から microSD へコピーしてファイル取り出す方法もあります。

    1. ここでも nuttx_shell スケッチを使用します。

      スケッチを書き込んで実行すると、シリアルターミナルに NuttShell nsh> プロンプトが表示されます。

    2. ls コマンドで Flash 内のファイルリストを確認します。

      nsh> ls -l /mnt/spif
      gnss tracker8
    3. 対象となるログファイルを cp コマンドで microSD へコピーします。

      nsh> cp /mnt/spif/00000003.txt /mnt/sd0
      gnss trackerA

      microSD を介して GPS ロガーデータを取り出すことができました。
      Flash 内のファイルが不要になれば rm コマンドで削除することができます。

      nsh> rm /mnt/spif/00000003.txt
  • Zmodem ファイル転送を使用する方法

    Spresense SDK 環境を使って Zmodem 転送機能によりファイルを取り出す方法もあります。
    詳細は Zmodem を使ったファイル転送 を参照してください。

取り出した GPS ロガーデータをもとに地図上にマップする方法は こちら を参照してください。

2.4. GPS トラッカーを省電力化する

2.4.1. 概要

GPS トラッカーに対して LowPower ライブラリを組み合わせることによって省電力化を行います。 Spresense は通常動作時でも低電力であることが特徴ですが、LowPower ライブラリを使用することによって、 さらに大幅に消費電力を削減することができます。 バッテリー/電池を長持ちさせるために、本チュートリアルにしたがってソースコードを変更してみてください。

ここでは LowPower ライブラリの以下の機能を使用します。 LowPower ライブラリの詳細については、 LowPower ライブラリ を参照してください。

  • クロックモードを変更する

  • スリープモードを使用する

2.4.2. クロックモードを変更する

通常、156MHz で動作しているシステム全体のクロックを 32MHz へ落とすことで電力を削減します。 GNSS 測位機能は、32MHz のクロック動作モードに落としても動作します。 GPS トラッカーを以下のように変更してください。

#include <LowPower.h> (1)

void setup()
{
  :
  LowPower.begin();                     (2)
  LowPower.clockMode(CLOCK_MODE_32MHz); (3)
  :
}
1 LowPower ライブラリを include します。
2 setup() 関数内で LowPower.begin() 関数を呼び出します。
3 クロックモードを 32MHz に設定します。

変更は LowPower.clockMode(CLOCK_MODE_32MHz) の呼び出しを追加するだけでクロックモードが切り替わります。

2.4.3. スリープモードを使用する

測位機能を間欠的に動作させる場合、非測位期間中にシステムをスリープ状態に落として待機電力を削減します。 スリープ状態に入る前に最終測位位置や衛星軌道情報をフラッシュメモリに保存しておくことで、 スリープから起床後はホットスタートにより直ちに測位動作を継続することができます。

スリープモードを使用するためにソースコードを次のように変更します。

#include <LowPower.h> (1)
#include <Flash.h>    (2)
1 LowPower ライブラリを include します。
2 Flash ライブラリを include します。
void setup()
{
  LowPower.begin();                      (1)
  bootcause_e bc = LowPower.bootCause(); (2)

  if ((bc == POR_SUPPLY) || (bc == POR_NORMAL)) {
    /* Remove backup file if power-on-reset */
    Flash.remove("gnss_backup.bin");     (3)
  }
1 setup() 関数内で LowPower.begin() 関数を呼び出します。
2 システムが起動した要因を取得します。電源投入時やリセットスイッチ押下など Power-On-Reset による起動なのか、 スリープ状態から起床したのかを判別することができます。
3 初回起動時など起動要因が Power-On-Reset の場合は Flash 上のバックアップファイルを削除します。 Deep Sleep から起床したときは Flash 上のバックアップファイルを使用してホットスタート測位を行います
static void SleepIn(void)
{
  /* Turn off the LED. */
  APP_PRINT("Sleep ");
  ledOff(PIN_LED0);
  /* Save backup data to flash */
  Gnss.saveEphemeris(); (1)
  Gnss.stop();
  Gnss.end();
1 SleepIn() 関数内で Gnss を終了する前にバックアップデータを Flash に保存します。
void loop() {
    :
  /* Check state. */
  if (State == eStateSleep)
  {
    /* Deep sleep */
    Serial.print("Go to Deep sleep...");
    LowPower.deepSleep(Parameter.SleepSec); (1)
1 loop() 関数で Sleep する際に LowPower.deepSleep() 関数を呼び出します。 Parameter.SleepSec 後に自動的に Deep Sleep から起床します。 Deep Sleep から起床したときは、リセットを押下したときと同様のシーケンスで、setup() から動作を開始します。

2.5. NMEA センテンスを出力する

2.5.1. 概要

GPS受信機で一般的に使用される NMEA データをシリアルモニタに出力します。

2.5.2. サンプルコード

NMEA データを出力するためのサンプルスケッチ gnss_nmea.ino を以下に示します。

#include <GNSS.h>
#include <gpsutils/cxd56_gnss_nmea.h>

SpGnss Gnss;
GnssPositionData PositionData;

/* NMEA buffer */
static char nmea_buf[NMEA_SENTENCE_MAX_LEN];

/* NMEA callback functions */
static char *reqbuf(uint16_t size)
{
  /* Get the pointer to NMEA buffer */
  if (size > sizeof(nmea_buf)) {
    return NULL;
  }
  return nmea_buf;
}

static void freebuf(char *buf)
{
  /* do nothing */
}

static int outbin(char *buf, uint32_t len)
{
  /* unused dummy function */
  return len;
}

static int outnmea(char *buf)
{
  /* Output NMEA to serial monitor */
  return Serial.print(buf);
}

void setup()
{
  /* Initialize Serial */
  Serial.begin(115200);

  /* Initialize GNSS */
  if (Gnss.begin()) {
    Serial.println("begin error!");
  }

  /* select satellite system */
  Gnss.select(GPS);
  Gnss.select(GLONASS);
  Gnss.select(QZ_L1CA);
  Gnss.select(QZ_L1S);

  /* Set interval */
  Gnss.setInterval(1);

  /* Start GNSS */
  if (Gnss.start()) {
    Serial.println("start error!");
  }

  /* Initialize NMEA library */
  NMEA_InitMask();

  /* Select NMEA sentence */
  NMEA_SetMask(NMEA_GGA_ON | (1)
               NMEA_GLL_ON |
               NMEA_GSA_ON |
               NMEA_GSV_ON |
               NMEA_GNS_ON |
               NMEA_RMC_ON |
               NMEA_VTG_ON |
               NMEA_ZDA_ON);

  /* Register callbacks to output NMEA */
  NMEA_OUTPUT_CB  funcs;
  funcs.bufReq  = reqbuf;
  funcs.out     = outnmea;
  funcs.outBin  = outbin;
  funcs.bufFree = freebuf;
  NMEA_RegistOutputFunc(&funcs);
}

void loop()
{
  /* Wait for GNSS data to be updated */
  if (Gnss.waitUpdate(-1)) {
    /* Get the GNSS data */
    Gnss.getPositionData(&PositionData);
    /* Convert to NMEA */
    NMEA_Output(&PositionData.Data);
  }
}
1 NMEA_SetMask() 関数で出力する NMEA センテンスを選択できます。 例えば、GGA, GSA のみ出力する場合は NMEA_GGA_ON | NMEA_GSA_ON (=0x5) を指定し、QZQSM のみ出力する場合は NMEA_QZQSM_ON (=0x4000) を指定します。 NMEA マスク値の詳細については後述します。

このサンプルスケッチを動作させるとシリアルモニタに NMEA データが出力されます。

gnss nmea log

2.5.3. NMEA マスク値について

NMEA 0183 (ver 4.00) 規格で定義されている NMEA センテンスのうち、 出力する NMEA センテンスをビットマスク値を NMEA_SetMask() 関数の引数に指定します。 初期状態では NMEA マスクには 0xef が設定されています。

NMEA Bit Description

$xxGGA

NMEA_GGA_ON(0)

時刻、緯度経度、標高、測位状態、DGPS基地局番号などの基本情報

$xxGLL

NMEA_GLL_ON(1)

時刻、緯度経度、測位状態などGGAの簡易版

$xxGSA

NMEA_GSA_ON(2)

衛星毎の使用不使用、DOP値

$xxGSV

NMEA_GSV_ON(3)

可視衛星の衛星番号、仰角、方位角、信号強度

$xxGNS

NMEA_GNS_ON(4)

時刻、緯度経度、測位状態

$xxRMC

NMEA_RMC_ON(5)

時刻、緯度経度、速度、時期偏差

$xxVTG

NMEA_VTG_ON(6)

移動速度に関する詳細情報

$xxZDA

NMEA_ZDA_ON(7)

年月日を含む時刻情報

$QZQSM

NMEA_QZQSM_ON(14)

災害危機管理通報サービスメッセージ(QZSS独自センテンス)

xx は、以下を表します。

  • GP:GPS衛星で測位している場合

  • GL:GLONASS衛星で測位している場合

  • QZ または GQ:QZSS衛星で測位している場合

  • BD または GB:BeiDou衛星で測位している場合

  • GA:Galileo衛星で測位している場合

  • GN:複数の衛星システムを利用して測位している場合

2.5.4. NMEA モニタ

NMEA データを利用した様々なツールが利用できます。

ここでは NMEA Monitor for windows を使用して NMEA データをデコードした結果をリアルタイムに表示してみます。

シリアルモニタ出力の COM ポートを選択して起動します。

gnss nmea monitor com ja

以下に示すように、緯度経度情報の他にも高度や天空図など多くの情報を確認することができます。

gnss nmea monitor ja

2.6. QZSS 災危通報を出力する

2.6.1. 概要

QZSS 災危通報 (QZQSM) の NMEA センテンスを出力します。

災害・危機管理通報サービスの仕様書はこちらのサイトに公開されています。

2.6.2. サンプルコード

QZQSM センテンスを出力するためのサンプルスケッチ gnss_qzqsm.ino を以下に示します。

#include <GNSS.h>
#include <gpsutils/cxd56_gnss_nmea.h>

SpGnss Gnss;
GnssPositionData PositionData;

/* NMEA buffer */
static char nmea_buf[NMEA_SENTENCE_MAX_LEN];

/* NMEA callback functions */
static char *reqbuf(uint16_t size)
{
  /* Get the pointer to NMEA buffer */
  if (size > sizeof(nmea_buf)) {
    return NULL;
  }
  return nmea_buf;
}

static void freebuf(char *buf)
{
  /* do nothing */
}

static int outbin(char *buf, uint32_t len)
{
  /* unused dummy function */
  return len;
}

static int outnmea(char *buf)
{
  /* Output NMEA to serial monitor */
  return Serial.print(buf);
}

void setup()
{
  /* Initialize Serial */
  Serial.begin(115200);

  /* Initialize GNSS */
  if (Gnss.begin()) {
    Serial.println("begin error!");
  }

  /* select satellite system */
  Gnss.select(GPS);
  //Gnss.select(GLONASS);
  Gnss.select(QZ_L1CA);
  Gnss.select(QZ_L1S);
  //Gnss.select(SBAS);

  /* Set interval */
  Gnss.setInterval(1);

  /* Start GNSS */
  if (Gnss.start()) {
    Serial.println("start error!");
  }

  /* Initialize NMEA library */
  NMEA_InitMask();

  /* Select NMEA sentence */
  NMEA_SetMask(NMEA_QZQSM_ON); // only QZQSM

  /* Register callbacks to output NMEA */
  NMEA_OUTPUT_CB  funcs;
  funcs.bufReq  = reqbuf;
  funcs.out     = outnmea;
  funcs.outBin  = outbin;
  funcs.bufFree = freebuf;
  NMEA_RegistOutputFunc(&funcs);
}

void loop()
{
  /* Wait for GNSS data to be updated */
  if (Gnss.waitUpdate(-1)) {
    /* Get the GNSS data */
    Gnss.getPositionData(&PositionData);
    /* Convert to NMEA */
    NMEA_Output(&PositionData.Data);

    /* Output QZQSM */
    void *handle = Gnss.getDCReport();
    if (handle) {
      NMEA_DcReport_Output((struct cxd56_gnss_dcreport_data_s*)handle);
    }
  }
}

このサンプルスケッチを動作させるとシリアルモニタに QZQSM センテンスが出力されます。

gnss qzqsm

2.7. GPS 時計を動かす

2.7.1. 概要

RTC ライブラリの Examples にあるサンプルスケッチ rtc_gnss.ino を動かします。
本サンプルでは、GNSS から得られた時刻情報を Spresense 内蔵の RTC (リアルタイムクロック) に設定します。

2.7.2. 動作環境

本スケッチの動作に必要な環境を以下に示します。
メインボードに GPS チップアンテナが搭載されており、メインボードだけで GPS 機能を動かすことができます。

  • Spresense メインボード

2.7.3. 動作手順

Arduino IDE メニューから File → Examples → RTC → rtc_gnss (ファイル → スケッチ例 → RTC → rtc_gnss) スケッチを開きます。

rtc gnss1
rtc gnss2

このスケッチを Spresense ボードに書き込みます。

rtc gnss3

シリアルモニタを開くと、時刻情報を出力します。

rtc gnss4

2.7.4. プログラム解説

#include <RTC.h>
#include <GNSS.h>

SpGnss Gnss;

#define MY_TIMEZONE_IN_SECONDS (9 * 60 * 60) // JST (1)
1 サンプルスケッチ上で時差を定義することで UTC 時刻からローカル時刻へ変換してシリアルに出力します。 RTC ライブラリの詳細は、RTC ライブラリ を参照してください。

2.8. GPS 1PPS 出力を行う

2.8.1. 概要

GNSS ライブラリの Examples にあるサンプルスケッチ gnss.ino をベースに 1PPS 信号の出力を行います。 1PPS とは衛星から取得される時刻をもとに正確な1秒周期の信号を出力します。

2.8.2. 動作環境

本スケッチの動作に必要な環境を以下に示します。
メインボードに GPS チップアンテナが搭載されています。1PPS 信号は、拡張ボードの D02 ピンから出力します。

  • Spresense メインボード

  • Spresense 拡張ボード

2.8.3. 動作手順

動作手順は、GPS サンプルスケッチ を参照してください。

2.8.4. プログラム解説

  /* Start 1PSS output to PIN_D02 */
  Gnss.start1PPS(); (1)
1 gnss.ino スケッチの setup() 関数でコメントアウトされている start1PPS() 関数を有効にしてください。 GPS 時刻が取得できたタイミングで PIN_D02 ピンへ 1PPS 信号の出力を開始します。

2.9. GNSS Add-on ボードを使用する

2.9.1. 概要

GNSS Add-onボードを使用するためのサンプルスケッチが提供されています。

  • gnss_addon.ino : GNSS Add-onボードを使用して測位した時刻、位置情報をシリアルへ出力します

  • gnss_addon_nmea.ino : GNSS Add-onボードを使用して測位した情報をNMEA形式でシリアルへ出力します

  • gnss_addon_qzqsm.ino : GNSS Add-onボードを使用してみちびき災危通報をNMEA形式でシリアルへ出力します

内蔵GNSSを用いたアプリケーションをGNSS Add-onボードで動作させる場合、スケッチ上の SpGnss クラスを SpGnssAddon クラスへ変更するだけで簡単に移植することができます。

みちびき災危通報を受信するためには、GNSS Add-on ボード上のファームウェアを v00.144 以降へ更新してください。

2.9.2. 動作環境

本スケッチの動作に必要な環境を以下に示します。 詳細は、GNSS Add-onボードの接続方法 を参照してください。

  • Spresense メインボード

  • Spresense GNSS Add-onボード

  • GNSSアクティブアンテナ

2.9.3. プログラム解説

  • クラス名が SpGnssAddon を用いて Gnss インスタンスを宣言します。 SpGnss の派生クラスになっており、測位を開始する、停止する、測位データを取得するなど、基本的な使い方は、前述した GPS サンプルスケッチ と同じです。

    static SpGnssAddon Gnss;
  • 測位周期

      /* Set interval */
      Gnss.setInterval(SpInterval_1Hz); (1)
    1 setInterval() 関数を用いて測位周期を変更することができます。測位周期を変更したい場合は引数を変更してください。デフォルトは1Hzで、1秒に1回の間隔で測位を行います。最大10Hzまで指定可能です。
    測位周期(Hz) 引数

    1

    SpInterval_1Hz

    2

    SpInterval_2Hz

    4

    SpInterval_4Hz

    5

    SpInterval_5Hz

    8

    SpInterval_8Hz

    10

    SpInterval_10Hz

  • 測位衛星選択はデフォルトでL1/L5帯を含む全衛星が固定で選択されているので特にユーザが指定する必要はありません。

    シリアルに出力される numSat: の値はGNSSライブラリの制約によって表示の上限が最大24個までとなっていますが、実際にはそれ以上の衛星を使って測位を行います。個別の衛星情報を参照したい場合、NMEA出力用のサンプルスケッチ gnss_addon_nmea.ino を使用して確認することができます。

  • 1PPS信号を出力する方法は、GPS 1PPS 出力を行う と同じです。

    GNSS Add-onボードの場合、1PPS信号はGNSS Add-onボード上の CL2 ランドから出力されます。その他に、ノーマウントの抵抗 R16 をショートすることで GNSS Add-on ボード上の PIN_I2S0_DATA_OUT ピンからも出力されます。詳しくは、ハードウェア設計資料 のGNSS Add-onボード回路図を参照してください。

3. Camera チュートリアル

3.1. Camera のサンプルを試してみる

3.1.1. 概要

Spresense カメラで撮影した JPEG 画像を microSD カードに保存します。

タイムラプスカメラのように約 1 秒おきにシャッターを切り、JPEG 画像 100 枚分の写真を撮影します。

3.1.2. 動作環境

  • Spresense メイン&拡張ボード

  • Spresense カメラボード

  • microSD カード

SpresenseメインボードとSpresenseカメラボード/HDR カメラボードの接続方法と準備 を参考に、Spresense メインボードとカメラボードを接続してください。

カメラ機能は拡張ボード無しでも動作しますが、このスケッチではカメラで撮った写真を microSD カードに保存するために拡張ボードを使用しています。

3.1.3. 動作手順

  1. microSD カードを拡張ボードの microSD カードスロットに挿入します。

  2. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Camera → camera を選択してサンプルスケッチを開きます。

    arduino camera examples1
    arduino camera examples2
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

    arduino camera examples3
  4. 書き込みが完了すると、プログラムが動作して写真を撮りはじめます。
    シリアルモニタを開くと、プレビュー画像のログ情報や、静止画撮影したときのログが表示されます。

    arduino camera examples4
  5. microSD カードに "PICT000.JPG" ~ "PICT009.JPG" ファイルとして保存されています。
    microSD カードから JPEG 画像を取り出して確認してみてください。

3.1.4. プログラム解説

詳しい解説は、開発ガイド サンプルコードによる解説 に載っています。

3.2. Camera プレビュー画像を LCD に表示する

3.2.1. 概要

さきほどの camera サンプルスケッチを変更して、プレビュー画像を LCD に表示します。

プレビュー画像のサイズは QVGA (320 x 240) です。

LCD は ILI9341 搭載 2.2 インチ液晶モジュールを使用します。

3.2.2. 動作環境

  • Spresense メイン&拡張ボード

  • Spresense カメラボード

  • microSD カード

  • LCD ボード (ILI9341 搭載 2.2 インチ液晶モジュール)

3.2.2.1. LCD と拡張ボードの接続

ILI9341 搭載 2.2 インチ液晶モジュールとの接続例を以下に示します。

connect ili9341
ILI9341 CXD5602 pin name Arduino compatible pin name

VCC

3.3V

3.3V / IOREF / AREF

GND

GND

GND

CS

SPI4_CS_X

D10

RESET

GPIO

D8

DC/RS

GPIO

D9

SDI/MOSI

SPI4_MOSI

D11

SCK

SPI4_SCK

D13

LED

3.3V

3.3V / IOREF / AREF

SDO/MISO

SPI4_MISO

D12

  • Spresense 拡張ボード を参考に、拡張ボードの JP1 ジャンパにて入出力端子基準電圧は 3.3V に設定してください。

  • ILI9341 の VCC 端子に 3.3V を供給します。Spresense の 3.3V, IOREF, AREF のいずれかに接続します。
    このサンプルでは ILI9341 の LED 端子にも 3.3V を常時供給しています。

  • ILI9341 との通信は、SPI (SPI4) を使用します。

  • RESETDC/RS ピンは GPIO として D8, D9 を使用します。
    参考) LCDSP001 基板 も同じピンアサインになっているため、このチュートリアルのコードをそのまま使用することができます。

3.2.2.2. Adafruit ILI9341 ライブラリのインストール方法

Adafruit ILI9341 の Arduino ライブラリをインストール方法について示します。

  1. こちらから 2 つのライブラリの zip ファイルをダウンロードします。

  2. Arduino IDE から スケッチ → ライブラリをインクルード → .ZIP形式のライブラリをインストール…​ を選択します。

    arduino camera examples5
  3. ダウンロードした zip ファイルを指定してインストールします。

  4. インストールに成功すると、以下のメッセージが表示されます。
    (Adafruit-GFX-Library と Adafruit_ILI9341 をそれぞれをインストールしてください)

    arduino camera examples6

3.2.3. 動作手順

  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Camera → camera を選択してサンプルスケッチを開きます。

  2. サンプルスケッチを編集するので、ファイル → 名前を付けて保存 を選び任意の名前で保存します。

  3. スケッチに次のコードを追加します。

    1. Adafruit_ILI9341 オブジェクトの定義をスケッチの先頭に追加します。

      #include <SPI.h>
      #include <Adafruit_GFX.h>
      #include <Adafruit_ILI9341.h>
      
      #define TFT_CS -1 (1)
      #define TFT_RST 8 (2)
      #define TFT_DC  9 (3)
      
      Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST); (4)
      1 SPI の CS はハードウェアによる自動制御を使用するので -1 を指定します
      2 RST ピンに 8 を指定します
      3 DC ピンに 9 を指定します
      4 tft オブジェクトを生成します。
    2. setup() 関数の中で、tft の初期化を行います。

      void setup(void) {
          :
        tft.begin(40000000); (1)
        tft.setRotation(3);  (2)
          :
      1 SPI の通信レートに 40 MHz を指定して tft を初期化します。
      2 LCD 表示を 270 度回転します。
    3. CamCB() 関数の中で、プレビュー画像を LCD に描画します。

      void CamCB(CamImage img)
      {
      
        /* Check the img instance is available or not. */
      
        if (img.isAvailable())
          {
            /* If you want RGB565 data, convert image data format to RGB565 */
      
            img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);
      
            tft.drawRGBBitmap(0, 0, (uint16_t *)img.getImgBuff(), 320, 240); (1)
          }
      }
      1 drawRGBBitmap() 関数を使用して、座標 (0, 0) から QVGA サイズ の RGB565 イメージを描画します。
  4. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

  5. スケッチがアップロードされ実行されると、写真撮影行うと同時に、LCD にプレビュー画像が表示されます。

3.2.4. プログラム解説

こちらも詳しい解説は、開発ガイド サンプルコードによる解説 を参照してください。

4. Sensing チュートリアル

4.1. Step Counter を使ってみよう

4.1.1. Step Counter サンプルスケッチを動かす

Step Counter サンプルスケッチを動作させるために事前に準備が必要なものを以下に示します。

  • センサーボード SPRESENSE用3軸加速度・3軸ジャイロ・気圧・温度センサ アドオンボード BMP280 BMI160搭載

  • BMI160-Arduinoライブラリ

    • BMI160-Arduino のライブラリのインストール方法について

      1. 上記サイトより「Clone or download」→「Download ZIP」を選択し ZIP ファイルを保存します。

      2. Arduino IDE のメニューから スケッチ → ライブラリをインクルード → .ZIP形式のライブラリをインストール…​ を選択し、保存しておいた ZIP ファイルを選択します。

      3. Arduino IDE に「ライブラリが追加されました」と表示されればインストール成功です。

  • ソニー製行動認識アルゴリズムStep Counter (AESM)

Step Counter サンプルスケッチでは、BMI160 ライブラリを使用した物理センサクライアントと、AESM を使用した論理センサクライアントが連携して動作します。BMI160 から得られた加速度データを入力し、論理センサクライアントにて機械学習を用いた行動認識を行い、その認識結果を出力します。

AESM は、Arduino IDE メニューの ツール > ブートローダを書き込む 操作によりインストールされます。 詳細は、Spresense ブートローダーのインストール を参照してください。

Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 Sensing → application → step_counter を選択してスケッチを起動します。

arduino stepcounter menu1
arduino stepcounter menu2

スケッチをコンパイルしてSpresenseボードに書き込みを行ってください。

シリアルモニタを開くと、歩数計及びその他の情報がリアルタイムに表示されます。

arduino stepcounter result
tempo

1秒当たりの歩数 [Hz]

stride

歩幅 [cm]
固定値を使用しています。

speed

動作速度 [m/s]

distance

動作距離 [m]
今後、GPS 測位結果をフュージョンして距離を算出するオプションを追加する予定です。
現在リリースしているソフトウエアでは GPS 結果は使用されていません。

step

歩数

move-type

行動認識結果 (stopping: 止まっている、walking: 歩いている、running: 走っている)

Spresense 基板を身に着けて歩いてみてください。

4.1.2. Step Counter の物理センサを変更してみる

ここでは Step Counter のサンプルスケッチで使用する物理センサを別のセンサへ変更してみます。

  • ローム社製センサーボード SPRESENSE-SENSOR-EVK-701

  • ローム社製ライブラリ KX122/KX126 Arduinoライブラリ

    • KX122/KX126 Arduinoライブラリのインストール方法について

      1. 上記サイトより「Clone or download」→「Download ZIP」を選択し ZIP ファイルを保存します。

      2. ダウンロードした ZIP ファイルを任意のフォルダに展開しておきます。

      3. Arduino IDE のメニューから スケッチ → ライブラリをインクルード → .ZIP形式のライブラリをインストール…​ を選択し、展開しておいたフォルダから KX122 もしくは KX126 フォルダを選択します。

      4. Arduino IDE に「ライブラリが追加されました」と表示されればインストール成功です。

step_counter.ino サンプルスケッチの変更箇所について説明します。

以下の例は、KX122 用の変更になります。KX126 をお使いの場合は、KX126 へ置き換えてください。

  • include ファイルの変更

    #include <BMI160Gen.h>

    を KX122 を include するように変更します。

    #include <Wire.h>
    #include <KX122.h>
    KX122 kx122(KX122_DEVICE_ADDRESS_1F);
  • setup() 関数の変更

    BMI160 の初期化について、

      /* Initialize device. */
    
      BMI160.begin(BMI160GenClass::I2C_MODE, i2c_addr);
    
      /* Set device setting */
    
      BMI160.setAccelerometerRange(accel_range);
      BMI160.setAccelerometerRate(accel_rate);

    KX122 の初期化へ変更します。KX122 ライブラリの accel_rate はデフォルトで 50Hz に設定されています。

      Wire.begin();
      kx122.init();
  • loop() 関数の変更

    加速度データ取得について、

      float x;
      float y;
      float z;
    
      /* Read raw accelerometer measurements from BMI160 */
    
      BMI160.readAccelerometerScaled(x, y, z);
    
      AccelSensor.write_data(x, y, z);

    KX122 からのデータ取得へ変更します。

      float acc[3];
    
      /* Read raw accelerometer measurements from KX122 */
    
      kx122.get_val(acc);
    
      AccelSensor.write_data(acc[0], acc[1], acc[2]);

上記の変更を行った後にスケッチをコンパイルして実行すると、KX122 を使った Step Counter が動作します。

4.1.3. Step Counterの独自論理センサを作ってみる

現在提供しているソニー製アルゴリズムのStep Counter(AESM) を使わずに、独自のStep Counterアルゴリズムを実装したい場合は、以下のように論理センサクライアントを作成します。

センサクライアントを継承した論理センサクラスを作成してください。

例えば、MyCounterClass をMyCounter.hに実装します。ヘッダファイルは必要に応じインクルードしてください。例えば、SensorClient.hは必要ですが、sensing/logical_sensor/step_counter.hはStepCounterの定義を使用したい場合のみ必要です。

また、例として、一般的に使われているライブラリの格納場所を下図に示します。

Diagram
図 5. MyCounterコード格納場所の例

これはWindows環境の例です。Arduino ディレクトリは、Ubuntu環境であれば ~/Arduino 、macOS環境であれば ~/Documents/Arduino となります。

class MyCounterClass : public SensorClient

この MyCounterClass

bool begin(int id,
           uint32_t subscriptions,
           int      rate,
           int      sample_watermark_num,
           int      size_per_sample));

int publish(FAR void* data,
            uint32_t  size_per_sample,
            uint32_t  freq,
            uint32_t  sample_watermark_num,
            uint32_t  timestamp);

int subscribe(sensor_command_data_mh_t& data);

をMyCounter.cppに実装(オーバーライド)します。

  • begin() の実装 :

    MyCounterの初期設定を行います。publishされた加速度センサのデータを受け取るためのcallback関数の登録が必要です。callback関数からはMyCounterの subscribe() をコールし加速度センサのデータを渡します。

    実装例
    /* Callback function to receive accel sensor data. */
    
    unsigned char mycounter_cb(sensor_command_data_mh_t &data)
    {
      /* MyCounter subscribes to the received accel. */
    
      return MyCounter.subscribe(data);
    }
    
    /* Initial setting of MyCounterClass. */
    
    bool MyCounterClass::begin(int      id,
                               uint32_t subscriptions,
                               int      rate,
                               int      sample_watermark_num,
                               int      size_per_sample)
    {
      /* Call super class begin() method. */
    
      return SensorClient::begin(id,
                                 subscriptions,
                                 rate,
                                 sample_watermark_num,
                                 size_per_sample,
                                 mycounter_cb);        /* <-- Register callback function. */
    }
  • subscribe() の実装 :

    加速度センサのデータをSubscribeし、独自のアルゴリズムで処理を行うことで、独自のStep Counterを作成することができます。

    今回、ライブラリが提供する AccelSensorClass を使う場合、publishされてくるデータは50Hzのデータが50サンプルまとまったもので、下記実装例中の st_accel_axis のように並んでいるx,y,z軸の加速度(単位は[G])データの配列になります。 このデータを取り出すために、ベースクラス( SensorClient ) の subscribe() を呼んでください。サンプリングレートとサンプル数は、 step_counter.ino スケッチ内でそれぞれ accel_rate , accel_sample_num で定義されています。
    その後、取り出したデータに対して独自の処理を施し、その結果をpublishすることになりますが、この例ですと SensorResultStepCounter 型で結果をpublishすることになります。個々のパラメータについては こちらを参照してください。独自のパラメータにてpublishをしたい場合にはこの限りではありませんのでこちらを参照してください。

    実装例
    /* Subscribe the accel sensor data which is published from AccelSenseorClass. */
    
    int MyCounterClass::subscribe(sensor_command_data_mh_t& data)
    {
      struct st_accel_axis
      {
        float x;
        float y;
        float z;
      } *accel_axis;
    
      /* Get accel sensor data via subscribe() of super class. */
    
      accel_axis = static_cast<accel_axis*>(SensorClient::subscribe(data));
      uint32_t  timestamp = data.time;
    
      /* In this sample, output parameter structure is SensorResultStepCounter. */
    
      SensorResultStepCounter output;
    
      /*
        独自のアルゴリズムを実装。
       outputに値を入れる。
       */
    
      /* Publish processed data to subscriber. */
    
      publish(&output,
              sizeof(SensorResultStepCounter),
              1, /* 1Hz */
              1, /* 1sample */
             timestamp);
    }
    subscribeされたデータは、 sensor_command_data_mh_t になっています。このデータ構造は こちら を参照してください。
  • publish() の実装 :

    publish() では、MyCounterで算出された値を発行することになります。 その読み出しアプリケーションクライアントのサンプルとして StepCounterReader が提供されており、それをそのまま使用する場合はpublishするデータ構造は、

    SensorResultStepCounter

    で送る必要があります。
    またその場合、パラメータのうち、処理結果を示す exec_resultSensorOK をセットしないと処理結果がアプリケーションまで応答されませんので注意してください。 StepCounterReader を使用しない場合はその限りではありません。

    上記のように SensorResultStepCounter の型で渡される場合は、 publish() は、ベースクラス( SensorClient )をそのまま使用して問題ありません。

SensorResultStepCounter 型は、以下になります。

/*--------------------------------------------------------------------------*/
/**
 * @struct StepCounterStepInfo
 * @brief the structure of STEP_COUNTER results.
 */
typedef struct {

  float    tempo;     /**< Indicates tempo of walking / jogging calculated
                       *   from the input acceleration data.
                       *   The unit is [Hz].
                       */
  float    stride;    /**< Indicates stride calculated
                       *   from input acceleration data.
                       *   The unit is [cm].
                       */
  float    speed;     /**< Indicates speed of walking / jogging calculated
                       *   from the input acceleration data.
                       *   The unit is [m/s].
                       */
  float    distance;  /**< Indicates cumulative travel distance calculated
                       *   from the input acceleration data.
                       *   The unit is [m].
                       */
  uint32_t step;      /**< Indicates the number of steps calculated
                       *   from the input acceleration data.
                       * The unit is [step].
                       */
  StepCounterMovementType  movement_type; /**<
                                    * Indicates the walking type calculated
                                    * from the input acceleration data.
                                    */
  uint64_t          time_stamp;    /**< Indicates latest timestamp of the
                                    *   acceleration sensor data used.
                                    */
} StepCounterStepInfo;

/*--------------------------------------------------------------------------*/
/**
 * @struct SensorResultStepCounter
 * @brief the structure of sensor result on step counter commands.
 */
typedef struct
{
  SensorExecResult exec_result; /**< Execute resule.  */

  union
    {
      StepCounterStepInfo steps;        /**< Step count.         */
      SensorAssertionInfo assert_info;  /**< Assert information. */
    };
} SensorResultStepCounter;

4.1.4. 自分の論理センサに合わせてデータを変えてみる

当然、独自の論理センサに合わせて、結果となるセンサデータの方を変えたいこともあるかもしません。その場合は、 MyCounterで、publishする際の、データ型を変更し、

int MyCounterClass::subscribe(sensor_command_data_mh_t& data)
{
  ..()..

  MyStepCounterResult output;

  publish(&output,
          sizeof(MyStepCounterResult),
          1, /* 1Hz */
          1, /* 1sample */
          timestamp);
}

読み出す側のApplication( StepCountReaderClass )で、

subscribe()
{
  MyStepCounterResult* steps = reinterpret_cast<MyStepCounterResult*>(ApplicationSensorClass.subscribe(data));
}

と変換してください。

4.2. 新しいセンサクライアントを作ってみる

4.2.1. 物理センサクライアントを作ってみる

prebuildされた Spresense SDK の中には、代表的なSensorのIDが割り当てられています。しかし、この定義されたSensorIDに該当しないような物理センサを作成したいというケースが、実際は大半になるかと思います。

ここでは、このように該当していない物理センサを使用するために、物理センサクライアントの実装例を示します。

今回は、BoschのセンサBME680を用いたガスセンサの物理センサクライアントを作成します。

2020年4月以降のArduino向けライブラリには、センシング用アルゴリズムライブラリがとりこまれています。
2020年4月現在、ArduinoIDEの最新バージョン(1.8.12)では、buildができなくなっています。ご注意ください。
https://github.com/arduino/arduino-builder/issues/353
SpresenseのCoreは、ARM Coretex-M4F ですので、"cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard" のライブラリを使用します。
  • BME680 Arduinoライブラリのインストール方法について

    1. 上記サイトより「Clone or download」→「Download ZIP」を選択し ZIP ファイルを保存します。

    2. Arduino IDE のメニューから スケッチ → ライブラリをインクルード → .ZIP形式のライブラリをインストール…​ を選択し、保存しておいた ZIP ファイルを選択します。

    3. Arduino IDE に「ライブラリが追加されました」と表示されればインストール成功です。

このセンサは、3秒または5分に1回、温度・湿度・気圧・ガスなどの環境センシングが可能です。

ガスセンサの物理センサクライアントを作成することで、センサフュージョン・エッジAIが可能になります。そのため、ガスセンサ用の物理センサクライアントを作成します。

enum
{
  SEN_selfID        = SensorClientID00, /*  0 */
  SEN_accelID       = SensorClientID01, /*  1 */
  SEN_accel1ID      = SensorClientID02, /*  2 */
  SEN_magID         = SensorClientID03, /*  3 */
  SEN_pressureID    = SensorClientID04, /*  4 */
  SEN_lightID       = SensorClientID05, /*  5 */
  SEN_pulseID       = SensorClientID06, /*  6 */
  SEN_tempID        = SensorClientID07, /*  7 */
  SEN_gyroID        = SensorClientID08, /*  8 */
  SEN_gnssID        = SensorClientID09, /*  9 */
  SEN_stepcounterID = SensorClientID10, /* 10 */
  SEN_tramID        = SensorClientID11, /* 11 */
  SEN_gestureID     = SensorClientID12, /* 12 */
  SEN_compassID     = SensorClientID13, /* 13 */
  SEN_barometerID   = SensorClientID14, /* 14 */
  SEN_tramliteID    = SensorClientID15, /* 15 */
  SEN_vadID         = SensorClientID16, /* 16 */
  SEN_wuwsrID       = SensorClientID17, /* 17 */
  SEN_adcID         = SensorClientID18, /* 18 */
  SEN_reserve19ID   = SensorClientID19, /* 19 */
  SEN_app0ID        = SensorClientID20, /* 20 */
  SEN_app1ID        = SensorClientID21, /* 21 */
  SEN_app2ID        = SensorClientID22, /* 22 */
  SEN_app3ID        = SensorClientID23, /* 23 */
  SEN_ID_MAX        = NumOfGeneralSensorClientID,
};

予約されているSensorIDの中で使用しないもの、例えば以下のIDを使います。

SEN_pulseID       = SensorClientID06, /*  6 */

次のようにSensorIDを定義して使います。

#define SensorClientID06 GasSensorID

次に、ガスセンサークライアントを作成します。 SensorClientクラスをベースクラスにして、以下のようにGasセンサのクラスを GasSensor.h などに定義します。

  • Header例(GasSenser.h)

class GasSensorClass : public SensorClient
{
public:
  bool begin(int      id,
             int      rate,
             int      sample_watermark_num,
             int      size_per_sample = sizeof(struct bsec_virtual_sensor_t));

  /**
   * @brief 1 Sample data write.
   */

  int write_data(float temp, float humi, float pres, float gas);

private:
  bool begin(int      id,
             uint32_t subscriptions        = 0,
             int      rate                 = 0,
             int      size_per_sample      = 0);

  struct gas_sensor_s
    {
      float temp;
      float humi;
      float pres;
      float gas;
    };

  int                   m_cnt;                   /* private counter */
  unsigned long         m_previous_time;
  MemMgrLite::MemHandle m_mh;

};

論理センサに publish したいデータを

  struct gas_sensor_s
    {
      float temp;
      float humi;
      float pres;
      float gas;
    };

のように定義し、こちらを送るような実装を行います。

  int write_data(float temp, float humi, float pres, float gas);

write の関数で、必要なデータの書き込みインタフェースを用意します。

今回のセンサは3秒に1回のセンシングなので、毎回センシングデータを publish する例にしています。
また、サンプリングレートに相当する rate はセンシングのInterval間隔を入れています。

実際にセンシングしたいデータは適宜変更してください。
サンプリングも同様に適宜変更してください。

内部の処理は、 GasSensor.cpp などに、

  • 実装例(GasSenser.cpp)

#include <GasSensor.h>
#include "Arduino.h"

#define GAS_INTERVAL_THRESHOLD  10000  /* [10s] */

Includeと、センシングしているデータが不連続であると判断するデータの間隔 GAS_INTERVAL_THRESHOLD を定義します。

bool GasSensorClass::begin(int      id,
                             uint32_t subscriptions,
                             int      rate, /* Interval time */
                             int      sample_watermark_num,
                             int      size_per_sample)
{

  m_cnt           = 0;
  m_previous_time = millis();

  if (ERR_OK != m_mh.allocSeg(
                         SENSOR_DATA_BUF_POOL,
                         size_per_sample * sample_watermark_num))
    {
      /* Fatal error occured. */

      printf("Fail to allocate segment of memory handle.\n");
      return false;
    }
  return true;
}

bool GasSensorClass::begin(int id,
                             int rate,
                             int size_per_sample)
{
  return begin(id, 0, rate, 1, size_per_sample);
}

begin を定義します。このセンサの場合、3秒単位か5分単位のセンシング間隔ですので、 rate は間隔をmsで設定するように実装してみています。 size_per_sample は、取りたいデータにより決まります。

int write_data(float temp, float humi, float pres, float gas);
{
  /* Check reading cycle */

  long  now           = millis();
  int   read_duration = m_rate * 1000;  /* 3s */
  int   diff          = now - m_previous_time;

  if (diff <= read_duration)
    {
      /* Do not get data. Because the interval of the cycle is short.
       * Return as a normal end
       */

      return SENSORCLIENT_ECODE_OK;
    }

  if (diff >= GAS_INTERVAL_THRESHOLD)
    {
       /* Input interval exceeded threshold. Clear the buffer. */

       m_previous_time = now;
       printf("Input interval exceeded threshold!\n");
    }
  else
    {
      /* Update previous time */

       m_previous_time += read_duration;
    }

  /* Read raw accelerometer measurements from BME680 */

  FAR struct gas_sensor_s *p_src =
    reinterpret_cast<struct gas_sensor_s *>(m_mh.getPa());

  p_src.temp = temp;
  p_src.humi = humi;
  p_src.pres = pres;
  p_src.gas  = gas;

  /* Check if the sample for one process has accumulated */

  publish(m_mh,
          sizeof(struct gas_sensor_s),
          m_rate,
          m_sample_watermark_num,
          now);

      /* Create new memory buffer. */


  if (ERR_OK != m_mh.allocSeg(
                  SENSOR_DATA_BUF_POOL,
                  sizeof(struct gas_sensor_s))
        {
          /* Fatal error occured. */
          printf("Fail to allocate segment of memory handle.\n");
          assert(0);
        }
    }

  return SENSORCLIENT_ECODE_OK;
}

GasSensorClass GasSensor;

write_data を定義します。今回は、温度、湿度、気圧、ガスの4つの値を取ります。
前回データを取得した時刻に対して、 read_duration 以下の場合は、センシングしないで抜けます。 read_duration を超えている場合は、データを取得します。 GAS_INTERVAL_THRESHOLD 以上離れてしまった場合は異常があったとしてエラー処理をします。

取得した各データは、

  FAR struct gas_sensor_s *p_src =
    reinterpret_cast<struct gas_sensor_s *>(m_mh.getPa());

で取得した共有メモリエリアに、

  p_src.temp = temp;
  p_src.humi = humi;
  p_src.pres = pres;
  p_src.gas  = gas;

このように gas_sensor_s 型で書き込みます。 書き込んだら、

  publish(m_mh,
          sizeof(struct gas_sensor_s),
          m_rate,
          m_sample_watermark_num,
          now);

で、publishします。 pulishしたら、新しい共有メモリを以下で確保します。

  if (ERR_OK != m_mh.allocSeg(
                  SENSOR_DATA_BUF_POOL,
                  sizeof(struct gas_sensor_s))
        {
          /* Fatal error occured. */
          printf("Fail to allocate segment of memory handle.\n");
          assert(0);
        }
    }

以上にのように物理センサクライアントを作成したら、実際にアプリケーションからセンシングしたデータを書き込むスケッチを作成します。

BSEC/examples/basicbasic.ino を参考に作成してください。

まず、こちらのIncludeを追加してください。

#include <MemoryUtil.h>
#include <SensorManager.h>
#include <GasSensor.h>

setup() 関数の中に

#define SensorClientID06 GasSensorID

void setup(void)
{
  GasSensor.begin(GasSensorID,3,sizeof(struct gas_sensor_s));

を追記します。

loop()関数内のprintしている個所に、 write_data を記載することで、

void loop(void)
{

 

    output += ", " + String(iaqSensor.breathVocEquivalent);
    output += ", " + String(iaqSensor.rawTemperature);
    output += ", " + String(iaqSensor.rawHumidity);
    output += ", " + String(iaqSensor.gasResistance);
    Serial.println(output);

    );

    GasSensor.write_data(iaqSensor.temperature,iaqSensor.humidity,iaqSensor.pressure,iaqSensor.staticIaq);

Sensor Manager にpublishされます。

4.2.2. 論理センサクライアントを作ってみる

[T.B.D]

5. LTE チュートリアル

LTE チュートリアルでは、各種サンプルスケッチの動かし方について解説しています。

5.1. モデムの情報を取得する

5.1.1. 概要

モデムから情報を取得するサンプルスケッチです。 本スケッチでは、モデムの電源をONにしてから、以下の情報を取得します。

  • IMEI

  • ファームウェアバージョン

5.1.2. 動作環境

  • Spresense メインボード

  • Spresense LTE拡張ボード

接続方法は、SpresenseメインボードとSpresense LTE拡張ボードの接続方法を参照してください。

5.1.3. 動作手順

  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 LTE → LteTestModem を選択してサンプルスケッチを開きます。

    arduino lte examples1
    arduino lte examples testmodem1
  2. ツール → シリアルポート で Spresense の COM ポートを選択して、書き込みを実行します。

  3. コンパイル&書き込み(Upload)の実行が終わったら、シリアルモニタを起動します。

  4. ボーレート 115200 bps を選択すると、下図に示すようにメッセージが表示されます。

    arduino lte examples testmodem2

5.1.4. プログラム解説

ステップとして、「初期設定」、「setup()」、「loop()」の3つが動作しています。 これら3つの動作について詳しく解説します。

5.1.4.1. 初期設定
  • 本スケッチで使用するライブラリのヘッダファイルをインクルードします。

    // libraries
    #include <LTE.h>  (1)
    1 モデムの情報を取得するために、LTEライブラリを使用するため、ヘッダファイル <LTE.h> をインクルードします。
  • 使用するクラスのインスタンスを初期化します。

    // initialize the library instance
    LTEModem modem;  (1)
    1 LTEModemクラスのインスタンスを初期化します。
  • 使用する変数を初期化します。

    // IMEI variable
    String IMEI = "";     (1)
    String VERSION = "";
    LTENetworkRatType RAT = LTE_NET_RAT_UNKNOWN;
    1 モデムのIMEI番号とファームウェアバージョン、RAT(Radio Access Technology)を格納する変数を定義します。
5.1.4.2. setup()
  • setup()関数ではSerialとモデムのセットアップを行います。

    void setup() {
      // initialize serial communications and wait for port to open:
      Serial.begin(115200);                          (1)
      while (!Serial) {
          ; /* wait for serial port to connect. Needed for native USB port only */
      }
    
      // start modem test (reset and check response)
      Serial.println("Starting modem test...");
      if (LTE_IDLE == modem.begin()) {               (2)
        Serial.println("modem.begin() succeeded.");
      } else {
        Serial.println("ERROR, no modem answer.");
      }
    }
    1 メッセージ表示用にSerialのセットアップを行います。
    2 LTEModemのbegin()メソッドを呼び出します。
    begin()メソッドを呼び出すことでモデムの電源をONにします。正常にモデムの電源がONになった際、begin()メソッドはLTE_IDLEを返します。
5.1.4.3. loop()
  • loop()関数ではモデムのIMEI番号とファームウェアバージョン、RATを取得し、シリアルモニタに表示します。

    void loop() {
    
      // IMEI of the modem
      IMEI = modem.getIMEI();                 (1)
      Serial.println("IMEI: " + IMEI);
    
      //Firmware version of the modem.
      VERSION = modem.getFirmwareVersion();   (2)
      Serial.println("VERSION: " + VERSION);
    
      // Current RAT of the modem.
      RAT = modem.getRAT();                   (3)
    
      switch (RAT) {
        case LTE_NET_RAT_CATM:
          Serial.println("RAT: LTE-M (LTE Cat-M1)");
          break;
        case LTE_NET_RAT_NBIOT:
          Serial.println("RAT: NB-IoT");
          break;
        default:
          Serial.println("RAT: Unknown type [" + String(RAT) + "]");
          break;
      }
    
      sleep(1);
    }
    1 LTEModemのgetIMEI()メソッドでモデムのIMEI番号を取得します。
    2 LTEModemのgetFirmwareVersion()メソッドでモデムのファームウェアバージョンを取得します。
    3 LTEModemのgetRAT()メソッドでモデムに設定されているRATを取得します。

5.2. ネットワークの情報を取得する

5.2.1. 概要

LTEネットワークから情報を取得するサンプルスケッチです。
本スケッチでは、LTEネットワークにモデムを登録してから、LTEネットワークから以下の情報を取得します。

  • IPアドレス

  • 受信強度

  • ネットワークキャリア名

5.2.2. 動作環境

  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

接続方法は、SpresenseメインボードとSpresense LTE拡張ボードの接続方法を参照してください。

本スケッチではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

5.2.3. 動作手順

  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 LTE → LteScanNetworks を選択してサンプルスケッチを開きます。

    arduino lte examples1
    arduino lte examples scannetworks1
  2. LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  3. 使用するRATを設定します。
    ご使用のSIMに合わせて、コメントアウト箇所の切り替えを行ってください。

    /* RAT to use
     * Refer to the cellular carriers information
     * to find out which RAT your SIM supports.
     * The RAT set on the modem can be checked with LTEModemVerification::getRAT().
     */
    
    #define APP_LTE_RAT (LTE_NET_RAT_CATM) // RAT : LTE-M (LTE Cat-M1) (1)
    // #define APP_LTE_RAT (LTE_NET_RAT_NBIOT) // RAT : NB-IoT  (2)
    1 LTE-M (LTE Cat-M1) で通信を行う場合はこの定義を使用してください。
    2 NB-IoT で通信を行う場合はこの定義を使用してください。
  4. ツール → シリアルポート で Spresense の COM ポートを選択して、書き込みを実行します。

  5. コンパイル&書き込み(Upload)の実行が終わったら、シリアルモニタを起動します。

  6. ボーレート 115200 bps を選択すると、下図に示すようにメッセージが表示されます。

    arduino lte examples scannetworks2

動作環境によっては通信状態が不安定になり、正しく動作しないことがあります。
詳細はFAQを確認してください。

5.2.4. プログラム解説

ステップとして、「初期設定」、「setup()」、「loop()」の3つが動作しています。 これら3つの動作について詳しく解説します。

5.2.4.1. 初期設定
  • 本スケッチで使用するライブラリのヘッダファイルをインクルードします。

    // libraries
    #include <LTE.h>  (1)
    1 LTEネットワークにモデムを登録するために、LTEライブラリを使用するため、ヘッダファイル <LTE.h> をインクルードします。
  • LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  • 使用するクラスのインスタンスを初期化します。

    // initialize the library instance
    LTE        lteAccess;        (1)
    LTEScanner scannerNetworks;  (2)
    1 LTEクラスのインスタンスを初期化します。
    2 LTEScannerクラスのインスタンスを初期化します。
5.2.4.2. setup()
  • setup()関数ではSerialとモデムのセットアップを行います。

    void setup() {
      // initialize serial communications and wait for port to open:
      Serial.begin(115200);                                                           (1)
      while (!Serial) {
        ; // wait for serial port to connect. Needed for Leonardo only
      }
    
      Serial.println("LTE networks scanner");
    
      while (true) {
    
        /* Power on the modem and Enable the radio function. */
    
        if (lteAccess.begin() != LTE_SEARCHING) {                                     (2)
          Serial.println("Could not transition to LTE_SEARCHING.");
          Serial.println("Please check the status of the LTE board.");
          for (;;) {
            sleep(1);
          }
        }
    
        /* The connection process to the APN will start.
         * If the synchronous parameter is false,
         * the return value will be returned when the connection process is started.
         */
        if (lteAccess.attach(APP_LTE_RAT,
                             APP_LTE_APN,
                             APP_LTE_USER_NAME,
                             APP_LTE_PASSWORD,
                             APP_LTE_AUTH_TYPE,
                             APP_LTE_IP_TYPE) == LTE_READY) {                         (3)
          Serial.println("attach succeeded.");
          break;
        }
    
        /* If the following logs occur frequently, one of the following might be a cause:
         * - APN settings are incorrect
         * - SIM is not inserted correctly
         * - If you have specified LTE_NET_RAT_NBIOT for APP_LTE_RAT,
         *   your LTE board may not support it.
         * - Rejected from LTE network
         */
        Serial.println("An error has occurred. Shutdown and retry the network attach process after 1 second.");
        lteAccess.shutdown();                                                         (4)
        sleep(1);
      }
    }
    1 メッセージ表示用にSerialのセットアップを行います。
    2 LTEのbegin()メソッドを呼び出します。 begin()メソッドを呼び出すことでモデムの電源をONにし、ネットワークをサーチします。
    正常にネットワークサーチが開始された際、begin()メソッドはLTE_SEARCHINGを返します。
    3 LTEのattach()メソッドを呼び出します。
    attach()メソッドを呼び出すことで、LTEネットワークにモデムを登録します。
    正常にLTEネットワークにモデムが登録された際、attach()メソッドはLTE_READYを返します。
    4 メソッドが期待の戻り値を返さない場合、LTEのshutdown()メソッドを呼び出します。
    shutdown()メソッドを呼び出すことで、モデムの電源をOFFにします。
5.2.4.3. loop()
  • loop()関数ではIPアドレスと接続したLTEネットワークキャリア名、LTEネットワークの受信強度を取得し、シリアルモニタに表示します。

    void loop() {
       // Assigned IP address
      IPAddress address = lteAccess.getIPAddress();         (1)
      Serial.print("IP address: ");
      Serial.println(address);
    
      // currently connected carrier
      Serial.print("Current carrier: ");
      Serial.println(scannerNetworks.getCurrentCarrier());  (2)
    
      // return signal strength
      Serial.print("Signal Strength: ");
      Serial.print(scannerNetworks.getSignalStrength());    (3)
      Serial.println(" [dBm]");
      sleep(1);
    }
    1 LTEのgetIPAddress()メソッドでLTEネットワークが割り当てたIPアドレスを取得します。
    2 LTEScannerのgetCurrentCarrier()メソッドで接続したLTEネットワークキャリア名を取得します。
    3 LTEScannerのgetSignalStrength()メソッドでLTEネットワークの受信強度を取得します。

5.3. HTTP GETメソッドをサーバに送信してデータを取得する

5.3.1. 概要

HTTP GETメソッドをLTEネットワークを介してサーバに送信し、データを取得するサンプルスケッチです。 本スケッチでは、LTEネットワークにモデムを登録し、サーバに対してHTTP GETメソッドを送信し、結果を取得します。

5.3.2. 動作環境

  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

接続方法は、SpresenseメインボードとSpresense LTE拡張ボードの接続方法を参照してください。

本スケッチではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

5.3.3. 動作手順

  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 LTE → LteWebClient を選択してサンプルスケッチを開きます。

    arduino lte examples1
    arduino lte examples webclient1
  2. LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  3. 使用するRATを設定します。
    ご使用のSIMに合わせて、コメントアウト箇所の切り替えを行ってください。

    /* RAT to use
     * Refer to the cellular carriers information
     * to find out which RAT your SIM supports.
     * The RAT set on the modem can be checked with LTEModemVerification::getRAT().
     */
    
    #define APP_LTE_RAT (LTE_NET_RAT_CATM) // RAT : LTE-M (LTE Cat-M1) (1)
    // #define APP_LTE_RAT (LTE_NET_RAT_NBIOT) // RAT : NB-IoT         (2)
    1 LTE-M (LTE Cat-M1) で通信を行う場合はこの定義を使用してください。
    2 NB-IoT で通信を行う場合はこの定義を使用してください。
  4. ツール → シリアルポート で Spresense の COM ポートを選択して、書き込みを実行します。

  5. コンパイル&書き込み(Upload)の実行が終わったら、シリアルモニタを起動します。

  6. ボーレート 115200 bps を選択すると、下図に示すようにメッセージが表示されます。

    arduino lte examples webclient2

動作環境によっては通信状態が不安定になり、正しく動作しないことがあります。
詳細はFAQを確認してください。

5.3.4. プログラム解説

ステップとして、「初期設定」、「setup()」、「loop()」の3つが動作しています。 これら3つの動作について詳しく解説します。

5.3.4.1. 初期設定
  • 本サンプルで使用するライブラリのヘッダファイルをインクルードします。

    // libraries
    #include <LTE.h>  (1)
    1 LTEネットワークにモデムを登録するために、LTEライブラリを使用するため、ヘッダファイル <LTE.h> をインクルードします
  • LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  • 使用するクラスのインスタンスを初期化します。

    // initialize the library instance
    LTE lteAccess;     (1)
    LTEClient client;  (2)
    1 LTEクラスのインスタンスを初期化します。
    2 LTEClientクラスのインスタンスを初期化します。
  • 接続先のサーバのパラメータを定義します。

    // URL, path & port (for example: arduino.cc)
    char server[] = "arduino.cc";                      (1)
    char path[] = "/asciilogo.txt";                    (2)
    int port = 80; // port 80 is the default for HTTP  (3)
    1 接続先サーバ の URL
    2 HTTP GET先 の サーバパス
    3 接続先サーバ の ポート番号
5.3.4.2. setup()
  • setup()関数ではSerialとモデムのセットアップを行います。

    void setup()
    {
      // initialize serial communications and wait for port to open:
      Serial.begin(115200);                                                           (1)
      while (!Serial) {
          ; // wait for serial port to connect. Needed for native USB port only
      }
    
      Serial.println("Starting web client.");
    
      while (true) {
    
        /* Power on the modem and Enable the radio function. */
    
        if (lteAccess.begin() != LTE_SEARCHING) {                                     (2)
          Serial.println("Could not transition to LTE_SEARCHING.");
          Serial.println("Please check the status of the LTE board.");
          for (;;) {
            sleep(1);
          }
        }
    
        /* The connection process to the APN will start.
         * If the synchronous parameter is false,
         * the return value will be returned when the connection process is started.
         */
        if (lteAccess.attach(APP_LTE_RAT,
                             APP_LTE_APN,
                             APP_LTE_USER_NAME,
                             APP_LTE_PASSWORD,
                             APP_LTE_AUTH_TYPE,
                             APP_LTE_IP_TYPE) == LTE_READY) {                         (3)
          Serial.println("attach succeeded.");
          break;
        }
    
        /* If the following logs occur frequently, one of the following might be a cause:
         * - APN settings are incorrect
         * - SIM is not inserted correctly
         * - If you have specified LTE_NET_RAT_NBIOT for APP_LTE_RAT,
         *   your LTE board may not support it.
         * - Rejected from LTE network
         */
        Serial.println("An error has occurred. Shutdown and retry the network attach process after 1 second.");
        lteAccess.shutdown();                                                         (4)
        sleep(1);
      }
    
      // if you get a connection, report back via serial:
      if (client.connect(server, port)) {                                             (5)
        Serial.println("connected");
        // Make a HTTP request:
        client.print("GET ");                                                         (6)
        client.print(path);
        client.println(" HTTP/1.1");
        client.print("Host: ");
        client.println(server);
        client.println("Connection: close");
        client.println();
      } else {
        // if you didn't get a connection to the server:
        Serial.println("connection failed");
      }
    }
    1 メッセージ表示用にSerialのセットアップを行います。
    2 LTEのbegin()メソッドを呼び出します。 begin()メソッドを呼び出すことでモデムの電源をONにし、ネットワークをサーチします。
    正常にネットワークサーチが開始された際、begin()メソッドはLTE_SEARCHINGを返します。
    3 LTEのattach()メソッドを呼び出します。
    attach()メソッドを呼び出すことで、LTEネットワークにモデムを登録します。
    正常にLTEネットワークにモデムが登録された際、attach()メソッドはLTE_READYを返します。
    4 メソッドが期待の戻り値を返さない場合、LTEのshutdown()メソッドを呼び出します。
    shutdown()メソッドを呼び出すことで、モデムの電源をOFFにします。
    5 LTEClientのconnect()メソッドを呼び出します。
    connect()メソッドを呼び出すことで、引数で指定したホスト名とポートに接続します。
    サーバへの接続が成功した際、connect()メソッドは1を返します。
    6 LTEClientのprint()メソッド、println()メソッドを使用して接続したサーバに対してHTTPリクエストを送信します。
5.3.4.3. loop()
  • loop()関数ではHTTPレスポンスを受信し、応答メッセージをシリアルに表示します。

    void loop()
    {
      // if there are incoming bytes available
      // from the server, read them and print them:
      if (int len = client.available()) {                (1)
        char buff[len + 1];
        buff[len] = '\0';
        client.read(buff, len);                          (2)
        Serial.print(buff);
      }
    
      // if the server's disconnected, stop the client:
      if (!client.available() && !client.connected()) {  (3)
        Serial.println();
        Serial.println("disconnecting.");
        client.stop();                                   (4)
    
        // do nothing forevermore:
        for (;;)
          sleep(1);
      }
    }
    1 LTEClientのavailable()メソッドを使用して、読み出し可能なバイト数を取得します。
    2 LTEClientのread()メソッドを使用して、接続したサーバから受信したデータをバッファに読み出し、シリアルに出力します。
    3 LTEClientのconnected()メソッドを使用して、クライアントがサーバに接続しているかどうかを取得します。
    4 LTEClientのstop()メソッドを使用して、サーバーから切断します。

5.4. TLSプロトコルを使用したHTTP Clientの動作を確認する

5.4.1. 概要

TLSプロトコルを使用したHTTP Clientのサンプルスケッチです。
本スケッチの内容は、LTEネットワークにモデムを登録してから、TLSプロトコルを使用してサーバに接続し、Arduino HTTP Clientライブラリが提供するget()メソッド・post()メソッドの動作を確認します。

LTEのファームウェアバージョンがRK_03_00_00_00_04121_001の場合、TLS通信APIが機能しない場合があります。( こちら をご参照ください)
ダウンロードサイトにある Updateツール を参照の上、アップデートをお願いします。

5.4.2. 動作環境

  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

  • microSD カード

接続方法は、SpresenseメインボードとSpresense LTE拡張ボードの接続方法を参照してください。

本スケッチではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

5.4.3. 動作手順

  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 LTE → LteHttpSecureClient を選択してサンプルスケッチを開きます。

    arduino lte examples1
    arduino lte examples httpsecureclient1
  2. Arduino IDE 上から ツール → ライブラリを管理…​ を選択します。
    検索欄に ArduinoHttpClient と入力し、結果にある ArduinoHttpClient by Arduino をインストールします。

    arduino lte examples httpsecureclient2
  3. LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  4. 使用するRATを設定します。
    ご使用のSIMに合わせて、コメントアウト箇所の切り替えを行ってください。

    /* RAT to use
     * Refer to the cellular carriers information
     * to find out which RAT your SIM supports.
     * The RAT set on the modem can be checked with LTEModemVerification::getRAT().
     */
    
    #define APP_LTE_RAT (LTE_NET_RAT_CATM) // RAT : LTE-M (LTE Cat-M1) (1)
    // #define APP_LTE_RAT (LTE_NET_RAT_NBIOT) // RAT : NB-IoT         (2)
    1 LTE-M (LTE Cat-M1) で通信を行う場合はこの定義を使用してください。
    2 NB-IoT で通信を行う場合はこの定義を使用してください。
  5. TLSプロトコルに必要なルート証明書をエクスポートします。
    手順はルート証明書のエクスポート手順を参考にしてください。

  6. microSDカードのディレクトリ構成とファイルの保存先を以下のようにしてください 。

    microSD:
      └CERTS/
        └httpbin-org.pem
  7. サンプルスケッチの ROOTCA_FILE をmicroSDカードに保存したファイル名に修正する必要があります。

    #define ROOTCA_FILE "CERTS/httpbin-org.pem" // Define the path to a file containing CA
                                                // certificates that are trusted.

    microSDカードに保存した ルート証明書 の ファイル名 を記述します。

  8. ツール → シリアルポート で Spresense の COM ポートを選択して、書き込みを実行します。

  9. コンパイル&書き込み(Upload)の実行が終わったら、シリアルモニタを起動します。

  10. ボーレート 115200 bps を選択すると、下図に示すようにメッセージが表示されます。

    arduino lte examples httpsecureclient3

making GET request から先に進まない場合は、お使いのSpresense LTE拡張ボードのファームウェアが古い可能性があります。
Updateツール を参照の上、最新のファームウェアへアップデートしてください。

動作環境によっては通信状態が不安定になり、正しく動作しないことがあります。
詳細はFAQを確認してください。

5.4.4. プログラム解説

ステップとして、「初期設定」、「setup()」、「loop()」の3つが動作しています。 これら3つの動作について詳しく解説します。

5.4.4.1. 初期設定
  • 本スケッチで使用するライブラリのヘッダファイルをインクルードします。

    // libraries
    #include <ArduinoHttpClient.h> (1)
    #include <RTC.h>               (2)
    #include <SDHCI.h>             (3)
    #include <LTE.h>               (4)
    1 HTTP GETメソッド・POSTメソッドの送信にArduino HTTP Client ライブラリを使用するため、ヘッダファイル <ArduinoHttpClient.h> をインクルードします。
    2 SPRESENSEの時刻を設定するために、RTC ライブラリを使用するため、ヘッダファイル <RTC.h> をインクルードします。
    3 TLSプロトコルに必要な証明書・秘密鍵を参照するために、SD ライブラリを使用するため、ヘッダファイル <SDHCI.h> をインクルードします。
    4 LTEネットワークにモデムを登録するために、LTEライブラリを使用するため、ヘッダファイル <LTE.h> をインクルードします。
  • LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  • 接続先のサーバのパラメータを定義します。

    // URL, path & port (for example: httpbin.org)
    char server[] = "httpbin.org";                       (1)
    char getPath[] = "/get";                             (2)
    char postPath[] = "/post";                           (3)
    int port = 443; // port 443 is the default for HTTPS (4)
    1 接続先サーバ の URL
    2 HTTP GET先 の サーバパス
    3 HTTP POST先 の サーバパス
    4 接続先サーバ の ポート番号
  • TLSプロトコルに必要なルート証明書・クライアント証明書・秘密鍵のパラメータを定義します。
    microSDカードに保存したファイル名に修正する必要があります。

    #define ROOTCA_FILE "path/to/cafile"   // Define the path to a file containing CA           (1)
                                           // certificates that are trusted.
    #define CERT_FILE   "path/to/certfile" // Define the path to a file containing certificate  (2)
                                           // for this client, if required by the server.
    #define KEY_FILE    "path/to/keyfile"  // Define the path to a file containing private key  (3)
                                           // for this client, if required by the server.
    1 microSDカードに保存した ルート証明書 の ファイル名 を記述してください。
    2 microSDカードに保存した クライアント証明書 の ファイル名 を記述してください。
    3 microSDカードに保存した 秘密鍵 の ファイル名 を記述してください。
  • 使用するクラスのインスタンスを初期化します。

    // initialize the library instance
    LTE lteAccess;
    LTETLSClient tlsClient;
    HttpClient client = HttpClient(tlsClient, server, port); (1)
    SDClass theSD;
    1 HttpClientのインスタンスを初期化する際に、LTETLSClientのインスタンスを指定します。
5.4.4.2. setup()

setup()関数の解説をします。

  • メッセージ表示用にSerialのセットアップを行います。

    // initialize serial communications and wait for port to open:
    Serial.begin(115200);
    while (!Serial) {
        ; // wait for serial port to connect. Needed for native USB port only
    }
    
    Serial.println("Starting secure HTTP client.");
  • theSD.begin()メソッドでmicroSDカードをマウントします。

    /* Initialize SD */
    while (!theSD.begin()) {
    ; /* wait until SD card is mounted. */
    }
  • LTEネットワークにモデムを登録します。

      while (true) {
    
        /* Power on the modem and Enable the radio function. */
    
        if (lteAccess.begin() != LTE_SEARCHING) {                      (1)
          Serial.println("Could not transition to LTE_SEARCHING.");
          Serial.println("Please check the status of the LTE board.");
          for (;;) {
            sleep(1);
          }
        }
    
        /* The connection process to the APN will start.
         * If the synchronous parameter is false,
         * the return value will be returned when the connection process is started.
         */
        if (lteAccess.attach(APP_LTE_RAT,
                             APP_LTE_APN,
                             APP_LTE_USER_NAME,
                             APP_LTE_PASSWORD,
                             APP_LTE_AUTH_TYPE,
                             APP_LTE_IP_TYPE) == LTE_READY) {          (2)
          Serial.println("attach succeeded.");
          break;
        }
    
        /* If the following logs occur frequently, one of the following might be a cause:
         * - APN settings are incorrect
         * - SIM is not inserted correctly
         * - If you have specified LTE_NET_RAT_NBIOT for APP_LTE_RAT,
         *   your LTE board may not support it.
         * - Rejected from LTE network
         */
        Serial.println("An error has occurred. Shutdown and retry the network attach process after 1 second.");
        lteAccess.shutdown();                                          (3)
        sleep(1);
      }
    1 lteAccess.begin()メソッドを使用して、LTEネットワークの探索を開始します。LTEネットワークの探索を開始するまで lteAccess.begin()メソッドを繰り返します。
    2 lteAccess.attach()メソッドを使用して、LTEネットワークにモデムを登録します。LTEネットワークにモデムが登録されるとループ処理を抜けます。
    3 LTEネットワークにモデムが登録できなかった場合、lteAccess.shutdown()メソッドを使用して、モデムの電源をOFFにし、 1秒ほどsleep()関数で待ってから、LTEネットワークの探索処理から再開します。

    ご使用のSIMにPINロックをかけている場合、begin()メソッドにPINロック解除コードを指定してください。

  • SPRESENSEに時刻を設定し、設定した時刻をシリアルに出力します。

    void printClock(RtcTime &rtc)                        (1)
    {
      printf("%04d/%02d/%02d %02d:%02d:%02d\n",
             rtc.year(), rtc.month(), rtc.day(),
             rtc.hour(), rtc.minute(), rtc.second());
    }
    
    void setup()
    {
      :
      // Set local time (not UTC) obtained from the network to RTC.
      RTC.begin();
      unsigned long currentTime;
      while(0 == (currentTime = lteAccess.getTime())) { (2)
        sleep(1);
      }
      RtcTime rtc(currentTime);                         (3)
      printClock(rtc);                                  (4)
      RTC.setTime(rtc);                                 (5)
    }
    1 引数で与えられた時刻をシリアルに出力するprintClock()関数を定義します。
    2 lteAccess.getTime()メソッドを使用して現地時刻(紀元からの秒数)を取得します。
    3 RtcTimeのインスタンスを現地時刻で生成します。
    4 printClock()関数を使用し、シリアルに現地時刻を出力します。
    5 RTC.setTime()メソッドを使用して、SPRESENSEに現地時刻を設定します。
5.4.4.3. loop()

loop関数の解説をします。

  • microSDカードに保存した証明書・秘密鍵をモデムに設定する。

    // Set certifications via a file on the SD card before connecting to the server
    File rootCertsFile = theSD.open(ROOTCA_FILE, FILE_READ);       (1)
    tlsClient.setCACert(rootCertsFile, rootCertsFile.available()); (2)
    rootCertsFile.close();
    
    // Remove these commented out if client authentication is required.
    //File certsFile = theSD.open(CERT_FILE, FILE_READ);
    //tlsClient.setCertificate(certsFile, certsFile.available());  (3)
    //certsFile.close();
    //File priKeyFile = theSD.open(KEY_FILE, FILE_READ);
    //tlsClient.setPrivateKey(priKeyFile, priKeyFile.available()); (4)
    //priKeyFile.close();
    1 theSD.open()メソッドを使用して、ルート証明書のFileインスタンスを生成します。
    2 tlsClient.setCACert()メソッドに、ルート証明書のFileインスタンスを指定して、モデムにルート証明書を設定します。
    3 クライアント証明書が必要な場合、ルート証明書と同様にFileインスタンスを生成して、 tlsClient.setCertificate()メソッドを使用して、モデムにクライアント証明書を設定します。
    4 秘密鍵が必要な場合、ルート証明書と同様にFileインスタンスを生成して、 tlsClient.setPrivateKey()メソッドを使用して、モデムに秘密鍵を設定します。
  • HTTP GETメソッドの動作を確認する。

    // HTTP GET method
    Serial.println("making GET request");
    client.get(getPath);                          (1)
    
    // read the status code and body of the response
    int statusCode = client.responseStatusCode(); (2)
    String response = client.responseBody();      (3)
    
    Serial.print("Status code: ");
    Serial.println(statusCode);                   (4)
    Serial.print("Response: ");
    Serial.println(response);                     (5)
    Serial.println("Wait five seconds");
    sleep(5);
    1 client.get()メソッドを使用して、サーバにTLSプロトコルで接続し、HTTP GETメソッドを送信します。
    2 client.responseStatusCode()メソッドを使用して、サーバから応答メッセージを受信し、受信したステータスコードを取得します。
    3 client.responseBody()メソッドを使用して、サーバからの応答メッセージのbody部を取得します。
    4 取得したステータスコードをシリアルに出力します。
    5 取得したメッセージのbody部をシリアルに出力します。
  • HTTP POSTメソッドの動作を確認する。

    // HTTP POST method
    Serial.println("making POST request");
    
    String contentType = "application/x-www-form-urlencoded"; (1)
    String postData = "name=Alice&age=12";                    (2)
    
    client.post(postPath, contentType, postData);             (3)
    
    // read the status code and body of the response
    statusCode = client.responseStatusCode();                 (4)
    response = client.responseBody();                         (5)
    
    Serial.print("Status code: ");
    Serial.println(statusCode);                               (6)
    Serial.print("Response: ");
    Serial.println(response);                                 (7)
    
    // do nothing forevermore:
    for (;;)
    sleep(1);
    1 POSTするメッセージのContent-Typeを指定します。
    2 POSTするメッセージのbody部を記述します。
    3 client.post()メソッドを使用して、サーバにTLSプロトコルで接続し、HTTP POSTメソッドを送信します。
    4 client.responseStatusCode()メソッドを使用して、サーバから応答メッセージを受信し、受信したステータスコードを取得します。
    5 client.responseBody()メソッドを使用して、サーバからの応答メッセージのbody部を取得します。
    6 取得したステータスコードをシリアルに出力します。
    7 取得したメッセージのbody部をシリアルに出力します。

5.5. UDPプロトコルを使用したNTP Clientの動作を確認する

5.5.1. 概要

UDPプロトコルを使用したNTP Clientのサンプルスケッチです。
本スケッチの内容は、LTEネットワークにモデムを登録してから、UDPプロトコルを使用してNTPサーバに接続し、Arduino NTP Clientライブラリが提供するupdate()メソッドの動作を確認します。

5.5.2. 動作環境

  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

接続方法は、SpresenseメインボードとSpresense LTE拡張ボードの接続方法を参照してください。

本スケッチではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

5.5.3. 動作手順

  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 LTE → LteNtpClient を選択してサンプルスケッチを開きます。

    arduino lte examples1
    arduino lte examples ntpclient1
  2. Arduino IDE 上から ツール → ライブラリを管理…​ を選択します。
    検索欄に NTPClient と入力し、結果にある NTPClient by Fabrice Weinberg をインストールします。

    arduino lte examples ntpclient2
  3. LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  4. 使用するRATを設定します。
    ご使用のSIMに合わせて、コメントアウト箇所の切り替えを行ってください。

    /* RAT to use
     * Refer to the cellular carriers information
     * to find out which RAT your SIM supports.
     * The RAT set on the modem can be checked with LTEModemVerification::getRAT().
     */
    
    #define APP_LTE_RAT (LTE_NET_RAT_CATM) // RAT : LTE-M (LTE Cat-M1) (1)
    // #define APP_LTE_RAT (LTE_NET_RAT_NBIOT) // RAT : NB-IoT         (2)
    1 LTE-M (LTE Cat-M1) で通信を行う場合はこの定義を使用してください。
    2 NB-IoT で通信を行う場合はこの定義を使用してください。
  5. ツール → シリアルポート で Spresense の COM ポートを選択して、書き込みを実行します。

  6. コンパイル&書き込み(Upload)の実行が終わったら、シリアルモニタを起動します。

  7. ボーレート 115200 bps を選択すると、下図に示すようにメッセージが表示されます。

    arduino lte examples ntpclient3

動作環境によっては通信状態が不安定になり、正しく動作しないことがあります。
詳細はFAQを確認してください。

5.5.4. プログラム解説

ステップとして、「初期設定」、「setup()」、「loop()」の3つが動作しています。 これら3つの動作について詳しく解説します。

5.5.4.1. 初期設定
  • 本スケッチで使用するライブラリのヘッダファイルをインクルードします。

    // libraries
    #include <NTPClient.h>  (1)
    #include <LTE.h>        (2)
    1 NTPサーバとの同期にArduino NTP Clientライブラリを使用するため、ヘッダファイル <NTPClient.h> をインクルードします。
    2 LTEネットワークにモデムを登録するために、LTEライブラリを使用するため、ヘッダファイル <LTE.h> をインクルードします。
  • LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  • NTPサーバ名、時刻オフセット、時刻の同期間隔を定義します。

    #define NTP_SERVER_NAME "ntp.nict.jp"                                                 (1)
    #define TIME_OFFSET     (9 * 60 * 60) // time offset in seconds(This example is JST)  (2)
    #define UPDATE_INTERVAL 60000         // update interval in milliseconds              (3)
    1 同期するNTPサーバ の ホスト名
    2 時刻オフセット値
    3 NTPサーバ との 時刻同期間隔
  • 使用するクラスのインスタンスを初期化します。

    // initialize the library instance
    LTE lteAccess;                                                                (1)
    LTEUDP ntpUDP;                                                                (2)
    
    // You can specify the time server pool and the offset (in seconds, can be
    // changed later with setTimeOffset() ). Additionaly you can specify the
    // update interval (in milliseconds, can be changed using setUpdateInterval() ).
    NTPClient timeClient(ntpUDP, NTP_SERVER_NAME, TIME_OFFSET, UPDATE_INTERVAL);  (3)
    1 LTEクラスのインスタンスを初期化します。
    2 LTEUDPクラスのインスタンスを初期化します。
    3 NTPClientクラスのインスタンスを初期化します。
5.5.4.2. setup()
  • setup()関数ではSerialとモデムのセットアップを行い、UDPクライアントを起動します。

    void setup()
    {
      // initialize serial communications and wait for port to open:
      Serial.begin(115200);                                                           (1)
      while (!Serial) {
          ; // wait for serial port to connect. Needed for native USB port only
      }
    
      Serial.println("Starting NTP client.");
    
      while (true) {
    
        /* Power on the modem and Enable the radio function. */
    
        if (lteAccess.begin() != LTE_SEARCHING) {                                     (2)
          Serial.println("Could not transition to LTE_SEARCHING.");
          Serial.println("Please check the status of the LTE board.");
          for (;;) {
            sleep(1);
          }
        }
    
        /* The connection process to the APN will start.
         * If the synchronous parameter is false,
         * the return value will be returned when the connection process is started.
         */
        if (lteAccess.attach(APP_LTE_RAT,
                             APP_LTE_APN,
                             APP_LTE_USER_NAME,
                             APP_LTE_PASSWORD,
                             APP_LTE_AUTH_TYPE,
                             APP_LTE_IP_TYPE) == LTE_READY) {                         (3)
          Serial.println("attach succeeded.");
          break;
        }
    
        /* If the following logs occur frequently, one of the following might be a cause:
         * - APN settings are incorrect
         * - SIM is not inserted correctly
         * - If you have specified LTE_NET_RAT_NBIOT for APP_LTE_RAT,
         *   your LTE board may not support it.
         * - Rejected from LTE network
         */
        Serial.println("An error has occurred. Shutdown and retry the network attach process after 1 second.");
        lteAccess.shutdown();                                                         (4)
        sleep(1);
      }
    
      timeClient.begin();                                                             (5)
    }
    1 メッセージ表示用にSerialのセットアップを行います。
    2 LTEのbegin()メソッドを呼び出します。 begin()メソッドを呼び出すことでモデムの電源をONにし、ネットワークをサーチします。
    正常にネットワークサーチが開始された際、begin()メソッドはLTE_SEARCHINGを返します。
    3 LTEのattach()メソッドを呼び出します。
    attach()メソッドを呼び出すことで、LTEネットワークにモデムを登録します。
    正常にLTEネットワークにモデムが登録された際、attach()メソッドはLTE_READYを返します。
    4 メソッドが期待の戻り値を返さない場合、LTEのshutdown()メソッドを呼び出します。
    shutdown()メソッドを呼び出すことで、モデムの電源をOFFにします。
    5 NTPClientのbegin()メソッドを使用して、NTPサーバにUDPプロトコルで接続します。
5.5.4.3. loop()
  • loop()関数ではNTPサーバとの同期を更新し、同期時刻をシリアルに表示します。

    void loop() {
      timeClient.update();  (1)
    
      Serial.println(timeClient.getFormattedTime());
    
      sleep(1);
    }
    1 NTPClientのupdate()メソッドを使用して、UPDATE_INTERVAL の更新間隔でNTPサーバと時刻を同期します。

5.6. AWSサーバにGNSS位置情報をPublishする

5.6.1. 概要

GNSS ライブラリ を使用し、取得した位置情報をMQTTプロトコルを使用してMQTTブローカにPublishするサンプルスケッチです。
本スケッチの内容は、LTEネットワークにモデムを登録してから、Arduino MQTT Clientライブラリを使用してMQTTブローカに接続します。GNSSライブラリを使用して測位を開始し、位置情報と時刻を含んだメッセージをArduino MQTT Clientライブラリを使用してMQTTブローカにPublishします。

LTEのファームウェアバージョンがRK_03_00_00_00_04121_001の場合、TLS通信APIが機能しない場合があります。( こちら をご参照ください)
ダウンロードサイトにある Updateツール を参照の上、アップデートをお願いします。

5.6.2. 動作環境

  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

  • microSD カード

接続方法は、SpresenseメインボードとSpresense LTE拡張ボードの接続方法を参照してください。

本スケッチではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

5.6.3. 動作手順

  1. AWS IoTを導入するため、 AWS アカウントの作成 をします。
    作成手順については AWS アカウント作成の流れ を参考にしてください。

  2. AWS IoT の使用を開始します。
    開始手順については AWS IoT Core の開始方法 を参考にしてください。

  3. 証明書をPCにダウンロードします。
    Thing オブジェクトの作成 の手順で発行した証明書をダウンロードします。ダウンロードするファイルは以下の3つです。

    • ルート証明書:Amazon_Root_CA_1.pem

    • クライアント証明書:c3c4ff2375.cert.pem

    • 秘密鍵:c3c4ff2375.private.key

      ファイル名は一例として記載しています。環境によって異なるため適宜読み替えてください。

  4. PCにダウンロードした証明書をmicroSDカードに保存します。
    microSDカードにCERTSというディレクトリを作成して上記3ファイルを格納します。

  5. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 LTE → LteGnssTracker を選択してサンプルスケッチを開きます。

    arduino lte examples1
    arduino lte examples gnsstracker1
  6. Arduino IDE 上から ツール → ライブラリを管理…​ を選択します。
    検索欄に ArduinoMqttClient と入力し、結果にある ArduinoMqttClient by Arduino をインストールします。

    arduino lte examples gnsstracker2
  7. LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    // APN data
    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  8. 使用するRATを設定します。
    ご使用のSIMに合わせて、コメントアウト箇所の切り替えを行ってください。

    /* RAT to use
     * Refer to the cellular carriers information
     * to find out which RAT your SIM supports.
     * The RAT set on the modem can be checked with LTEModemVerification::getRAT().
     */
    
    #define APP_LTE_RAT (LTE_NET_RAT_CATM) // RAT : LTE-M (LTE Cat-M1) (1)
    // #define APP_LTE_RAT (LTE_NET_RAT_NBIOT) // RAT : NB-IoT  (2)
    1 LTE-M (LTE Cat-M1) で通信を行う場合はこの定義を使用してください。
    2 NB-IoT で通信を行う場合はこの定義を使用してください。
  9. MQTTブローカの接続に必要な設定パラメータを記述します。

    MQTTブローカ名の確認方法を以下に示します。

    1. AWS IoT コンソールを開きます。

    2. [Manage (管理)]、[Things (モノ)] の順に選択します。事前に作成した IoT のモノを選択し、[Interact (操作)] を選択します。モノ の詳細ページの [HTTPS] セクションにエンドポイントが表示されます。

      // MQTT broker
      #define BROKER_NAME "your-MQTT-broker" // replace with your broker                     (1)
      #define BROKER_PORT 8883               // port 8883 is the default for MQTT over TLS.  (2)
      1 [HTTPS] セクション表示されたエンドポイントを記述してください。
      2 接続先の ポート を指定します。デフォルトは 8883 です。この手順ではデフォルトから変更する必要はありません。
  10. TLSプロトコルに必要なルート証明書・クライアント証明書・秘密鍵のパラメータを記述します。
    microSDカードに保存したファイル名に修正します。

    #define ROOTCA_FILE "CERTS/Amazon_Root_CA_1.pem"   // Define the path to a file containing CA           (1)
                                           // certificates that are trusted.
    #define CERT_FILE   "CERTS/c3c4ff2375.cert.pem" // Define the path to a file containing certificate  (2)
                                           // for this client, if required by the server.
    #define KEY_FILE    "CERTS/c3c4ff2375.private.key"  // Define the path to a file containing private key  (3)
                                           // for this client, if required by the server.
    1 microSDカードに保存した ルート証明書 の ファイル名 を記述します。
    2 microSDカードに保存した クライアント証明書 の ファイル名 を記述します。
    3 microSDカードに保存した 秘密鍵 の ファイル名 を記述します。

    ファイル名は例です。ご自身でダウンロードしたファイル名に書き換えてください。

  11. サンプルアプリケーションがPublishする トピック を記述します。
    AWS IoT MQTT クライアントでデバイスの MQTT メッセージを確認するでサブスクライブしたとトピックを記述します。

    // MQTT topic
    #define MQTT_TOPIC "spresense/gnss_tracker" // replace with your topic
  12. ツール → シリアルポート で Spresense の COM ポートを選択して、書き込みを実行します。

  13. コンパイル&書き込み(Upload)の実行が終わったら、シリアルモニタを起動します。

  14. ボーレート 115200 bps を選択します。
    正常にLTEネットワークにモデムが登録され、MQTTブローカーに接続が完了すると You’re connected to the MQTT broker! と表示されます。
    その後、測位が完了すると Position is fixed. と表示されます。
    測位完了から1秒おきに合計60回、測位データをPublishします。成功した場合は下図に示すようにメッセージが表示されます。

    arduino lte examples gnsstracker3

    Attempting to connect to the MQTT broker: から先に進まない場合は、お使いのSpresense LTE拡張ボードのファームウェアが古い可能性があります。
    Updateツール を参照の上、最新のファームウェアへアップデートしてください。

    動作環境によっては通信状態が不安定になり、正しく動作しないことがあります。
    詳細はFAQを確認してください。

  15. AWS IoT コンソールでメッセージがPublishされたか確認します。

  16. AWS IoT コンソールでサブスクライブしたメッセージをCSVファイルでエクスポートします。

  17. ダウンロードしたCSVファイルを加工しGPSロガーデータを作成します。パソコン等の専用ツールを使って、 移動の軌跡を地図上に重ねて閲覧表示するためです。

    • ダウンロードしたCSVからpayload部分 ( $GPGGA, から ,*xx まで ) のみを抽出します。 抽出した際のイメージを以下に示します。この文字列が合計60行続きます。

      $GPGGA,053156.05,3525.6925,N,13922.2242,E,1,08,1.1,47.8,M,,M,,*44
    • GPSロガーデータを確認します。
      GPS ロガーデータの確認手順 を参照してください。

5.6.4. プログラム解説

ステップとして、「初期設定」、「doAttach()」、「setup()」、「loop()」の4つが動作しています。 これら3つの動作について詳しく解説します。

5.6.4.1. 初期設定
  • 本スケッチで使用するライブラリのヘッダファイルをインクルードします。

    // APN data
    #include <RTC.h>               (1)
    #include <SDHCI.h>             (2)
    #include <GNSS.h>              (3)
    #include <LTE.h>               (4)
    #include <ArduinoMqttClient.h> (5)
    1 SPRESENSEの時刻を設定するために、RTC ライブラリを使用するため、ヘッダファイル <RTC.h> をインクルードします。
    2 TLSプロトコルに必要な証明書・秘密鍵を参照するために、SD ライブラリを使用するため、ヘッダファイル <SDHCI.h> をインクルードします。
    3 GNSSライブラリを使用して測位するために <GNSS.h>をインクルードします。
    4 LTEネットワークにモデムを登録するために、LTEライブラリを使用するため、ヘッダファイル <LTE.h> をインクルードします。
    5 MQTTでメッセージをPublishするために Arduino MQTT Clientライブラリ を使用するため、ヘッダファイル <ArduinoMqttClient.h> をインクルードします。
  • LTEネットワークにモデムを登録する際に必要なAPNパラメータを定義します。
    ご使用のSIMに合わせて、値を修正する必要があります。

    #define APP_LTE_APN "apn" // replace your APN                       (1)
    
    /* APN authentication settings
     * Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
     */
    #define APP_LTE_USER_NAME "user"     // replace with your username  (2)
    #define APP_LTE_PASSWORD  "password" // replace with your password  (3)
    1 ご使用のSIMに合わせた アクセスポイント名 を記述してください。
    2 ご使用のSIMに合わせた ユーザ名 を記述してください。
    3 ご使用のSIMに合わせた パスワード を記述してください。
  • MQTTブローカの接続に必要な設定パラメータを定義します。

    // MQTT broker
    #define BROKER_NAME "your-MQTT-broker" // replace with your broker                     (1)
    #define BROKER_PORT 8883               // port 8883 is the default for MQTT over TLS.  (2)
    1 MQTTブローカ名 を記述してください。
    2 接続先の ポート を指定します。TLSプロトコルを使ったMQTTのデフォルトは 8883 です。
  • TLSプロトコルに必要なルート証明書・クライアント証明書・秘密鍵の保存場所を定義します。

    #define ROOTCA_FILE "path/to/cafile"   // Define the path to a file containing CA           (1)
                                           // certificates that are trusted.
    #define CERT_FILE   "path/to/certfile" // Define the path to a file containing certificate  (2)
                                           // for this client, if required by the server.
    #define KEY_FILE    "path/to/keyfile"  // Define the path to a file containing private key  (3)
                                           // for this client, if required by the server.
    1 ルート証明書 の ファイル名 を記述してください。
    2 クライアント証明書 の ファイル名 を記述してください。
    3 秘密鍵 の ファイル名 を記述してください。

    例えばCERTSディレクトリを作成して cafile という名前で保存した場合は CERTS/cafile と記述します。

    必ずしもmicroSDカードに保存する必要はありません。SPI-Flashに保存したファイルを指定することも可能です。
    その場合は <Flash.h> をインクルードしてオブジェクト名を theSD から Flash に変更してください。
    theSD.beginでマウントが完了するのを待つ処理を削除してください。

  • サンプルアプリケーションがPublishする トピック を記述します。

    // MQTT topic
    #define MQTT_TOPIC "spresense/gnss_tracker" // replace with your topic
  • MQTTブローカにPublishするメッセージの間隔と回数を定義します。

    // MQTT publish interval settings
    #define PUBLISH_INTERVAL_SEC   1   // MQTT publish interval in sec (1)
    #define MAX_NUMBER_OF_PUBLISH  60  // Maximum number of publish    (2)
    1 MQTTブローカにメッセージをPublishする間隔(秒)を記述してください。
    2 MQTTブローカにメッセージをPublishする回数を記述してください。この回数に達するとPublishを終了する動作となります。
  • 使用するクラスのインスタンスを初期化します。

    // initialize the library instance
    LTE lteAccess;
    LTETLSClient client;           (1)
    MqttClient mqttClient(client); (2)
    SDClass theSD;
    SpGnss Gnss;
    1 LTETLSClientのインスタンスを初期化します。
    2 MqttClientのインスタンスを初期化する際に、LTETLSClientのインスタンスを指定します。

    LTETLSClientクラスからLTEClientクラスに変更することでプレーンなMQTTで動作させることが可能です。
    プレーンなMQTTの接続先ポート番号は 1883 です。また証明書の設定処理が不要となります。

5.6.4.2. doAttach()

doAttach()関数の解説をします。

  • LTEネットワークにモデムの登録処理を開始します。

    while (true) {
    
      /* Power on the modem and Enable the radio function. */
    
      if (lteAccess.begin() != LTE_SEARCHING) {                      (1)
        Serial.println("Could not transition to LTE_SEARCHING.");
        Serial.println("Please check the status of the LTE board.");
        for (;;) {
          sleep(1);
        }
      }
    
      /* The connection process to the APN will start.
        * If the synchronous parameter is false,
        * the return value will be returned when the connection process is started.
        */
      if (lteAccess.attach(APP_LTE_RAT,
                            APP_LTE_APN,
                            APP_LTE_USER_NAME,
                            APP_LTE_PASSWORD,
                            APP_LTE_AUTH_TYPE,
                            APP_LTE_IP_TYPE,
                            false) == LTE_CONNECTING) {              (2)
        Serial.println("Attempting to connect to network.");
        break;
      }
    
      /* If the following logs occur frequently, one of the following might be a cause:
        * - APN settings are incorrect
        * - SIM is not inserted correctly
        * - If you have specified LTE_NET_RAT_NBIOT for APP_LTE_RAT,
        *   your LTE board may not support it.
        */
      Serial.println("An error has occurred. Shutdown and retry the network attach preparation process after 1 second.");
      lteAccess.shutdown();                                          (3)
      sleep(1);
    }
    1 lteAccess.begin()メソッドを使用して、LTEネットワークの探索を開始します。LTEネットワークの探索を開始するまでlteAccess.begin()メソッドを繰り返します。
    2 lteAccess.attach()メソッドを使用して、LTEネットワークにモデムを登録します。第6引数の synchronousfalse に設定して、登録処理を非同期で実行します。これはモデムが登録される間に並行してGNSSの初期化処理を実行するためです。正しくモデムの登録が開始されるとループ処理を抜けます。
    3 LTEネットワークに登録開始できなかった場合、lteAccess.shutdown()メソッドを使用して、モデムの電源をOFFにし、1秒ほどsleep()関数で待ってから、LTEネットワークの探索処理から再開します。
5.6.4.3. setup()

setup()関数の解説をします。

  • メッセージ表示用にSerialのセットアップを行います。

    // Open serial communications and wait for port to open
    Serial.begin(115200);
    while (!Serial) {
        ; // wait for serial port to connect. Needed for native USB port only
    }
    
    Serial.println("Starting GNSS tracker via LTE.");
  • microSD カードがマウントされるまで待ちます。

    /* Initialize SD */
    while (!theSD.begin()) {
      ; /* wait until SD card is mounted. */
    }
  • doAttach()関数を呼び出してLTEネットワークにモデムの登録処理を開始します。

    /* Connect LTE network */
    doAttach();
  • GNSSの初期化処理を実行します。

    int result;
    
    /* Activate GNSS device */
    result = Gnss.begin();
    assert(result == 0);
    
    /* Start positioning */
    result = Gnss.start();
    assert(result == 0);
    Serial.println("Gnss setup OK");
  • LTEネットワークにモデムが登録されるまで待ちます。

    // Wait for the modem to connect to the LTE network.
    Serial.println("Waiting for successful attach.");
    LTEModemStatus modemStatus = lteAccess.getStatus(); (1)
    
    while(LTE_READY != modemStatus) {
      if (LTE_ERROR == modemStatus) {
    
        /* If the following logs occur frequently, one of the following might be a cause:
          * - Reject from LTE network
          */
        Serial.println("An error has occurred. Shutdown and retry the network attach process after 1 second.");
        lteAccess.shutdown();                           (2)
        sleep(1);
        doAttach();
      }
      sleep(1);
      modemStatus = lteAccess.getStatus();
    }
    
    Serial.println("attach succeeded.");
    1 lteAccess.getStatus()メソッドを使用して、モデムの状態を取得します。モデムの状態が LTE_READY に遷移したらループを抜けます。
    2 LTEネットワークに登録できなかった場合、lteAccess.shutdown()メソッドを使用して、モデムの電源をOFFにし、1秒ほどsleep()関数で待ってから、LTEネットワークの探索処理から再開します。
  • SPRESENSEに時刻を設定し、設定した時刻をシリアルに出力します。

    void printClock(RtcTime &rtc)                        (1)
    {
      printf("%04d/%02d/%02d %02d:%02d:%02d\n",
             rtc.year(), rtc.month(), rtc.day(),
             rtc.hour(), rtc.minute(), rtc.second());
    }
    
    void setup()
    {
      :
      // Set local time (not UTC) obtained from the network to RTC.
      RTC.begin();
      unsigned long currentTime;
      while(0 == (currentTime = lteAccess.getTime())) { (2)
        sleep(1);
      }
      RtcTime rtc(currentTime);                         (3)
      printClock(rtc);                                  (4)
      RTC.setTime(rtc);                                 (5)
      :
    }
    1 引数で与えられた時刻をシリアルに出力するprintClock()関数を定義します。
    2 lteAccess.getTime()メソッドを使用して現地時刻(紀元からの秒数)を取得します。
    3 RtcTimeのインスタンスを現地時刻で生成します。
    4 printClock()関数を使用し、シリアルに現地時刻を出力します。
    5 RTC.setTime()メソッドを使用して、SPRESENSEに現地時刻を設定します。
  • microSD カードに保存したルート証明書・クライアント証明書・秘密鍵を読み込み、MQTTブローカに接続します。

    // Set certifications via a file on the SD card before connecting to the MQTT broker
    File rootCertsFile = theSD.open(ROOTCA_FILE, FILE_READ);    (1)
    client.setCACert(rootCertsFile, rootCertsFile.available()); (2)
    rootCertsFile.close();
    
    File certsFile = theSD.open(CERT_FILE, FILE_READ);          (3)
    client.setCertificate(certsFile, certsFile.available());    (4)
    certsFile.close();
    
    File priKeyFile = theSD.open(KEY_FILE, FILE_READ);          (5)
    client.setPrivateKey(priKeyFile, priKeyFile.available());   (6)
    priKeyFile.close();
    
    Serial.print("Attempting to connect to the MQTT broker: ");
    Serial.println(broker);
    
    if (!mqttClient.connect(broker, port)) {                    (7)
      Serial.print("MQTT connection failed! Error code = ");
      Serial.println(mqttClient.connectError());
      // do nothing forevermore:
      for (;;)
        sleep(1);
    }
    
    Serial.println("You're connected to the MQTT broker!");
    1 theSD.open()メソッドを使用して、ルート証明書のFileインスタンスを生成します。
    2 client.setCACert()メソッドに、ルート証明書のFileインスタンスを指定して、モデムにルート証明書を設定します。
    3 theSD.open()メソッドを使用して、クライアント証明書のFileインスタンスを生成します。
    4 client.setCertificate()メソッドに、クライアント証明書のFileインスタンスを指定して、モデムにクライアント証明書を設定します。
    5 theSD.open()メソッドを使用して、秘密鍵のFileインスタンスを生成します。
    6 client.setPrivateKey()メソッドに、秘密鍵のFileインスタンスを指定して、モデムに秘密鍵を設定します。
    7 mqttClient.connect()メソッドを使用して、MQTTブローカーに接続します。
5.6.4.4. loop()

loop関数の解説をします。

  • GNSSからの受信を待ち測位結果を取得します。

    void loop()
    {
      /* Check update. */
      if (Gnss.waitUpdate(-1)) {   (1)
        /* Get navData. */
        SpNavData navData;
        Gnss.getNavData(&navData); (2)
    1 Gnss.waitUpdate() 関数でGNSS からの受信を待ちます。
    2 Gnss.getNavData() 関数で測位結果を取得します。
  • 測位しているかどうか判定します。測位していた場合、MQTTブローカーにPublishします。

    bool posFix = ((navData.posDataExist) && (navData.posFixMode != 0)); (1)
    if (posFix) {
      Serial.println("Position is fixed.");
      String nmeaString = getNmeaGga(&navData);                          (2)
      if (strlen(nmeaString.c_str()) != 0) {
        unsigned long currentTime = lteAccess.getTime();
        if (currentTime >= lastPubSec + PUBLISH_INTERVAL_SEC) {          (3)
          // Publish to broker
          Serial.print("Sending message to topic: ");
          Serial.println(topic);
          Serial.print("Publish: ");
          Serial.println(nmeaString);
    
          // send message, the Print interface can be used to set the message contents
          mqttClient.beginMessage(topic);                                (4)
          mqttClient.print(nmeaString);                                  (5)
          mqttClient.endMessage();                                       (6)
          lastPubSec = currentTime;
          numOfPubs++;
        }
      }
    } else {
      Serial.println("Position is not fixed.");
    }
    1 Gnss.getNavData() 関数で得られた navData をもとに測位しているかどうか判定します。
    2 getNmeaGga() 関数を使用してNMEA 0183 フォーマットの GPGGA センテンスを取得します。
    3 lteAccess.getTime() 関数を使用して現在の時刻を取得します。取得した時刻が前回Publishした時刻+Publish間隔よりも進んでいた場合にPublishする処理を実行します。
    4 mqttClient.beginMessage() 関数を使用して対象のトピック名に対してのMQTTメッセージを構します。
    5 mqttClient.print() 関数を使用してGPGGA センテンスをMQTTメッセージに設定します。
    6 mqttClient.endMessage() 関数を使用して構築したMQTTメッセージをMQTTブローカーにPublishします。

5.7. ルート証明書のエクスポート手順

5.7.1. 概要

HTTPSなどTLSによるセキュアな通信を使ったサーバへアクセスするためには、ルート証明書をダウンロードしてアプリケーションで指定した場所にコピーしておく必要があります。

Spresenseでは、以下の証明書に対応しています。

  • DER encoded binary X.509 (DER)

    DER形式のバイナリ証明書ファイル。拡張子は .cer

  • Base 64 encoded X.509 (PEM)

    Base 64形式のASCIIテキスト証明書ファイル。拡張子は .pem / .cer

使用する証明書のサイズは 1999 バイト以下である必要があります。PEM のテキスト形式で 2000 バイトを超える場合は、DER のバイナリ形式の証明書を使用してください。

ここでは、サーバアクセス用ルート証明書のダウンロード方法について説明します。

5.7.2. ダウンロード方法

以下の手順でダウンロードします。 以下の手順ではWindows/Chromeを使っていますが、他のOSやブラウザの場合でも同様の手順で対応している形式を選んでダウンロードしてください。

  1. アクセスしたいサイトのページを開きます。

    ブラウザでアクセスしたいページを開きます。以下はアクセスしたいページが https://example.com/ の例です。

    tutorial lte tls https open
  2. サイトの証明書を表示します。

    1. ページURL部分に表示されているセキュリティ通信ボタンをクリックします。

    2. セキュリティ保護のメニューを開きます。

    3. 有効な証明書を開きます。

      tutorial lte tls certification view ja
  3. ルート証明書を表示します。

    1. 証明書のパス(階層)を表示します。

    2. 最上位層(ルート)の証明書を選択します。

    3. 証明書の表示をクリックします。

      tutorial lte tls root certification view ja
  4. ルート証明書をコピー(エクスポート)します。

    1. 証明書のコピー(エクスポート)を選択します。

      tutorial lte tls root certification export 1 ja
    2. エクスポートファイルの形式を選択するページまで移動します。

      tutorial lte tls root certification export 2 ja
    3. エクスポートするファイルの形式を選択します。

      Spresenseで対応している DER encoded binary X.509 または Base 64 encoded X.509 を選択します。

      tutorial lte tls root certification export 3 ja
    4. エクスポート先を選択します。

      tutorial lte tls root certification export 4 ja
    5. エクスポートを実行します。

      tutorial lte tls root certification export 5 ja

以上の手順を実行することでルート証明書がコピー(エクスポート)されます。 そしてコピーされたルート証明書をアプリケーションで指定した場所にコピーすることでSpresenseでセキュア通信を行うことができます。

6. AI チュートリアル

6.1. 手書き文字認識

Arduino IDE の ファイル → スケッチ例 → Spresense用のスケッチ例 DNNRT → number_recognition に 簡単な数字認識のサンプルスケッチが提供されています。

詳しくは、開発ガイドの DNNRTライブラリ を参照してください。 Neural Network Console を使って学習モデルを作成する方法や、 それを組み込んで実際に Spresense ボード上で認識器として動かす手順について書かれています。

また、その他のサンプルについてもこのチュートリアルに随時追加される予定です。

7. Arduino マルチコア環境

Arduino IDE を使ったマルチコアプログラミング開発環境について説明します。

Arduino マルチコア環境では、MultiCore MP ライブラリを使用します。 アプリケーション CPU には、全部で 6 個のコアがあり、1 個の MainCore と 5 個の SubCore で構成されています。 通常のシングルコアアプリケーションでは、ユーザーは MainCore のみを使ってプログラミングします。 一方、ここで説明するマルチコア環境を用いることで、ユーザーは SubCore を含むすべてのコアに対して 任意のアプリケーションプログラムを書いて実行させることができるようになります。

MainCore

MainCore は電源投入時に必ず起動されます。

SubCore(s)

SubCore は MainCore からの制御によって起動されます。全部で 5 個の SubCore があります。

本チュートリアルでは、MultiCore MP ライブラリに付属の 2 種類の Example を動かします。

マルチコア Boot サンプル

4 個の SubCore を起動し、マルチコアを使って L チカを行います。
このサンプルを動作させることで、SubCore を起動するための手順を理解することができます。

マルチコア MessageHello サンプル

MainCore と SubCore 間でメッセージのやり取りを行います。
このサンプルを動作させることで、MainCore と SubCore との通信方法について理解することができます。

7.1. マルチコア Boot サンプルを動かしてみる

主な手順としては、

  1. Arduino IDE を使用してスケッチを開く

  2. コアを選択する

  3. コンパイル&アップロードする

これを動作させたいコアの数だけ繰り返します。

以下の手順にしたがって実行してみてください。

  • デスクトップから Arduino IDE を起動します
    (デスクトップにアイコンが無い場合はスタートメニュー等から起動してください)

    arduino multicore desktop
    マルチコアプログラミングで、複数の Arduino IDE ウィンドウを起動する際に注意事項があります。
    Arduino IDE のメニューから File → New (ファイル → 新規作成) でウィンドウを新規作成した場合、例えば、一つのウィンドウで Core 選択のメニューを切り替えると、その設定が連動して全てのウィンドウに反映されてしまいます。
    これを回避する方法として、Core ごとに複数の Arduino IDE ウィンドウを開くときは、毎回デスクトップ上のアイコンから Arduino IDE を起動してください。そうすることで、Core 選択などのメニュー設定が連動することなくウィンドウごとに独立して設定できるようになります。
  • MainCore プログラミング

    • Arduino IDE メニューから File → Examples → Examples for Spresense → MultiCore MP → Boot → Main (ファイル → スケッチ例 → Spresense用のスケッチ例 → MultiCore MP → Boot → Main) スケッチを開きます

      arduino multicore boot0
      arduino multicore boot1 main
    • Main.ino スケッチの解説

      MP.begin(subid) により、引数 subid で指定した SubCore を起動します。
      ここでは、SubCore1 ~ SubCore4 の計 4 個の SubCore を起動しています。

      arduino multicore boot4 main
    • Arduino IDE メニューから Tools → Core → MainCore (ツール → Core → MainCore) を選択します

      arduino multicore boot2 main

      下のステータスバーで MainCore が選択されていることを確認できます

      arduino multicore boot3 main
      Arduino IDE 1.8.9 ~ 1.8.13 のバージョンではメニューを切り替えたときに
      ステータスバーが正しく表示されなくなるという不具合があります
    • Upload ボタンを押して、Compile & Upload を行います

      arduino multicore boot5 main
      複数ウィンドウから複数コアのプログラムを同時に Upload することはできません。
      Upload 動作が同時に実行されないようにコアごとに順番に行ってください。
    • Compile 結果のログに使用メモリサイズが表示されます

      MainCore は常に 768 KByte のメモリを使用します。

      arduino multicore boot6 main

      正常に Upload が完了すれば、MainCore プログラミングは完了です

      • 試しに SubCore プログラムがまだ存在しない状態でシリアルモニタを起動してみます。
        SubCore が存在しないので、MP.begin(1~4) の呼び出しがエラーで返ってきていることが分かります。

        arduino multicore boot7 serial err

        シリアルモニタを開いている状態だと、他のウィンドウからの Upload が正しく行えないことがあります。
        この確認を終えたら、シリアルモニタを閉じてください。

続いて SubCore のプログラミングを行います。

  • SubCore プログラミング

    • Arduino IDE メニューから File → Examples → Examples for Spresense → MultiCore MP → Boot → Sub1 (ファイル → スケッチ例 → Spresense用のスケッチ例 → MultiCore MP → Boot → Sub1) スケッチを開きます

      arduino multicore boot0
      arduino multicore boot1 sub1
    • Sub1.ino スケッチの解説

      setup() で MP.begin() を呼び出して、MainCore に起動完了を通知します。
      loop() では MPLog() によるログ表示と LED0 の点滅制御を行っています。

      arduino multicore boot4 sub1
    • Arduino IDE メニューから Tools → Core → SubCore 1 (ツール → Core → SubCore 1) を選択します

      arduino multicore boot2 sub1

      下のステータスバーで SubCore 1 が選択されていることを確認できます

      arduino multicore boot3 sub1
      Arduino IDE 1.8.9 ~ 1.8.13 のバージョンではメニューを切り替えたときに
      ステータスバーが正しく表示されなくなるという不具合があります
    • Upload ボタンを押して、Compile & Upload を行います

      arduino multicore boot5 sub1
      複数ウィンドウから複数コアのプログラムを同時に Upload することはできません。
      Upload 動作が同時に実行されないようにコアごとに順番に行ってください。
    • Compile 結果のログに使用メモリサイズが表示されます

      ここで使用する SubCore サンプルスケッチは 128 KByte のメモリを使用することが分かります。
      このサイズは、ユーザープログラムに依存して増減します。

      arduino multicore boot6 sub1

      正常に Upload が完了すれば、SubCore 1 のプログラミングは終了です

  • SubCore 2, 3, 4 についても同様の手順で、Compile & Upload を行ってください

    • Sub2 スケッチを開き、SubCore 2 を選択して、Compile & Upload

    • Sub3 スケッチを開き、SubCore 3 を選択して、Compile & Upload

    • Sub4 スケッチを開き、SubCore 4 を選択して、Compile & Upload

  • 動作確認

    • Spresense メイン基板上の 4 個の緑 LED が点滅します

    • シリアルモニタを開くと、各コアからのログ出力が表示されます

      arduino multicore boot7 serial
  • まとめ

    • 簡単なサンプルスケッチを使って SubCore の起動方法について解説しました。
      既存のスケッチをマルチコア環境に移植するときは MP.begin() の呼び出しを追加してください。

    • Arduino IDE の Compile & Upload 方法など、基本的な使い方はマルチコア環境でも特に変わりはありません。 主な差分は、Tools → Core (ツール → Core) でコア選択のメニューが追加されていることです。

7.2. マルチコア MessageHello サンプルを動かしてみる

主な手順は、マルチコア Boot サンプルと同じです。
このサンプルでは、MainCore と SubCore(s) で共通のスケッチを使用します。

  1. Arduino IDE を使用してスケッチを開く(一度だけ)

  2. コアを選択する

  3. コンパイル&アップロードする

これを動作させたいコアの数だけ繰り返します。 このサンプルでは、全て (計 5 個) の SubCore を動かします。

以下の手順にしたがって実行してみてください。

  • デスクトップから Arduino IDE を起動します
    (デスクトップにアイコンが無い場合はスタートメニューから起動してください)

    arduino multicore desktop
    マルチコアプログラミングで、複数の Arduino IDE ウィンドウを起動する際に注意事項があります。
    Arduino IDE のメニューから File → New (ファイル → 新規作成) でウィンドウを新規作成した場合、例えば、一つのウィンドウで Core 選択のメニューを切り替えると、その設定が連動して全てのウィンドウに反映されてしまいます。
    これを回避する方法として、Core ごとに複数の Arduino IDE ウィンドウを開くときは、毎回デスクトップ上のアイコンから Arduino IDE を起動してください。そうすることで、Core 選択などのメニュー設定が連動することなくウィンドウごとに独立して設定できるようになります。
  • MainCore プログラミング

    • Arduino IDE メニューから File → Examples → MultiCore MP → Messsage → MessageHello (ファイル → スケッチ例 → Spresense用のスケッチ例 → MultiCore MP → Message → MessageHello) スケッチを開きます

      arduino multicore boot0
      arduino multicore hello1
    • MessageHello.ino スケッチの解説

      スケッチ内で #ifdef SUBCORE ~ #else ~ #endif を使って、MainCore, SubCore の実装を切り替えています。

      • MainCore ソースコードについて

        setup() で MP.begin(subid) により、5 つの SubCore を起動しています。
        setup() の最後に MP.RecvTimeout(MP_RECV_POLLING) により受信をポーリングモードに設定しています。
        このモードにすると MP.Recv() を呼び出したときに受信待ちに入らずポーリング動作を行います。
        loop() では、MP.Recv() で各 SubCore から packet のアドレスを受信し、packet 内の message を printf() 出力しています。

        arduino multicore hello4 main
      • SubCore のソースコードについて

        setup() で MP.begin() を呼び出して、MainCore に起動完了を通知します。
        loop() では、packet に "Hello" message を格納し、そのアドレスを MP.Send() で MainCore へ送信しています。

        arduino multicore hello4 sub
    • Arduino IDE メニューから Tools → Core → MainCore (ツール → Core → MainCore) を選択します

      arduino multicore hello2 main

      下のステータスバーで MainCore が選択されていることを確認できます

      arduino multicore boot3 main
      Arduino IDE 1.8.9 ~ 1.8.13 のバージョンではメニューを切り替えたときに
      ステータスバーが正しく表示されなくなるという不具合があります
    • Upload ボタンを押して、Compile & Upload を行います

      arduino multicore hello5 main
      複数ウィンドウから複数コアのプログラムを同時に Upload することはできません。
      Upload 動作が同時に実行されないようにコアごとに順番に行ってください。
    • Compile 結果のログに使用メモリサイズが表示されます

      MainCore は常に 768 KByte のメモリを使用します

      arduino multicore boot6 main

      正常に Upload が完了していれば、MainCore プログラミングは終了です。

続いて SubCore のプログラミングを行います

  • SubCore プログラミング

    • Arduino IDE メニューから Tools → Core → SubCore 1 (ツール → Core → SubCore 1) を選択します

      arduino multicore hello2 sub1

      下のステータスバーで SubCore 1 が選択されていることを確認できます

      arduino multicore boot3 sub1
      Arduino IDE 1.8.9 ~ 1.8.13 のバージョンではメニューを切り替えたときに
      ステータスバーが正しく表示されなくなるという不具合があります
    • Upload ボタンを押して、Compile & Upload を行います

      arduino multicore hello5 main
      複数ウィンドウから複数コアのプログラムを同時に Upload することはできません。
      Upload は同時に実行されないようにコアごとに順番に行ってください。
    • Compile 結果のログに使用メモリサイズが表示されます

      ここで使用する SubCore サンプルスケッチは 128 KByte のメモリを使用することが分かります。
      このサイズは、ユーザープログラムに依存して増減します。

      arduino multicore boot6 sub1

      正常に Upload が完了していれば、SubCore 1 のプログラミングは終了です

  • SubCore 2, 3, 4, 5 についても同様の手順で、Compile & Upload を行ってください

    • SubCore 2 を選択して、Compile & Upload

    • SubCore 3 を選択して、Compile & Upload

    • SubCore 4 を選択して、Compile & Upload

    • SubCore 5 を選択して、Compile & Upload

  • 動作確認

    • シリアルモニタを開いてください

    • 各 SubCore が送信したメッセージを MainCore が受信し、シリアルモニタ上に表示されます

      arduino multicore hello7 serial
  • まとめ

    • サンプルスケッチを使って SubCore との通信方法について解説しました。

    • コア間でメモリが共有されているので、アドレスを渡してメッセージ通信を行うことができます。

    • コアごとにスケッチを分けずに共通のスケッチを使用したマルチコアプログラミングも可能です。

7.3. 特記事項

  • MultiCore MP ライブラリについて、詳しくは 開発ガイド を参照してください。

  • Spresense 基板上に既にアップロード済みの SubCore バイナリがあり、それらを一括して消去したい場合は、Spresense ブートローダーのインストール を行ってください。ローダのインストール時に、一旦、すべての SubCore バイナリを削除してからローダのインストールが始まります。

    arduino multicore remove subcore

    なお、MainCore から MP.begin() を呼び出さない限り、勝手に SubCore が動作することはありません。 そのため、古い SubCore がアップロードされたまま残っていても特に問題はありません。

  • マルチコアプログラミングで、Core ごとに複数の Arduino IDE ウィンドウを開くときは、毎回デスクトップ上のアイコンから Arduino IDE を起動してください。そうすることで、Core 選択などのメニュー設定がウィンドウごとに独立して設定できるようになります。

  • Arduino IDE での複数起動や Core を切り替える操作が煩雑ですが、arduino-builder や arduino-cli といったコマンドラインツールを使用することも可能です。また、Upload についても、直接 flash_writer ツールを使用すれば、各コアのバイナリを一回のコマンドで一括してロードすることも可能です。コマンドラインを用いた方法について後日ドキュメントに掲載する予定です。

8. SignalProcessing チュートリアル

SignalProcessing ライブラリには、FFT/IIRのフィルタがあり、それらに対して、以下のサンプルスケッチが用意されています。

本ドキュメントでは、これらのサンプルスケッチの動かし方について解説しています。

8.1. PeakDetector

8.1.1. 概要

このサンプルスケッチはマイクで入力されている音声データをFFTライブラリでリアルタイム周波数解析を行うことで、リアルタイムにピーク周波数を検出します。 Audio 機能に関しては、 Audio ライブラリ をご参照ください。

また、本サンプルは、信号処理ライブラリをSubCoreにオフロードして処理を行っております。同時に MultiCore MP ライブラリ もご参照ください。

8.1.2. 動作環境

  • Spresense メイン&拡張ボード

  • 録音用マイク

マイクの接続については、以下のハードウェアガイドを参照してください。

提供しているサンプルでは4チャンネルのマルチチャンネルでの動作になっています。
マイク数は必要にあわせて接続し、アプリケーション内で調整してください。

8.1.3. 動作手順

主な動作手順の流れは以下になります。

  1. 信号処理を行うSubCoreをコンパイル&アップロード

  2. 音声取得&アプリケーション処理を行うMainCoreをコンパイル&アップロード

  3. アプリケーションの実行

MainCoreのコンパイル&アップロードから実施すると、起動後、SubCoreへの呼び出しを実行しエラーになります。
SubCoreへのコンパイル&アップロードをすでに実行していた場合、MainCore起動後は、既にアップロードされているSubCoreのプログラムが起動されます。
8.1.3.1. SubCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → PeakDetector → SubFFT を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → SubCore1 (ツール → Core → SubCore1) を選択します。

    本サンプルでは、オフロードしている SubCoreSubCore1 を使用しています。
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

    SubCore のプログラムは、 起動後、 MainCore からの指示を待つ状態になります。

これで、SubCore側の準備はOKです。

8.1.3.2. MainCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → PeakDetector → MainAudio を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → MainCore (ツール → Core → MainCore) を選択します

  3. マイコンボードへの書き込みを行います。

SubCore のアップロードでシリアルポートを選択している場合は、再度の選択は不要です。

これで、MainCore側の準備もOKです。

8.1.3.3. アプリケーションの実行
  1. MainCore のアップロードが終わったら、Tools → シリアルモニタ を開き アプリケーションを起動します。

  2. アプリケーションが起動されると、マイクから入力された各チャネルの音声を周波数解析し、ピーク周波数をシリアルモニタに表示し続けます。

8.1.4. プログラム解説

  • 入力音声のチャンネル数は、MainAudio.ino の中の

/* Select mic channel number */
//const int mic_channel_num = 1;
//const int mic_channel_num = 2;
const int mic_channel_num = 4;

で指定します。

このサンプルでは、 SubCore 側にある #define MAX_CHANNEL_NUM 4 は、MainAudio.inomic_channel_num と同じ値にする必要があります。

本サンプルスケッチでの SubCore のコードは、引数なしの FFT.begin() を呼んでおり、最大チャンネル数 = 実行チャンネル数になっています。
最大チャンネル数 ≠ 実行チャンネル数の場合は、FFT.begin(WindowHamming, 実行チャンネル数, (FFTLEN/2)) を呼んで、実行チャンネル数を指定してください。
  • FFTのTap数は、SubFFT.ino の中の

/* Select FFT length */

//#define FFT_LEN 32
//#define FFT_LEN 64
//#define FFT_LEN 128
//#define FFT_LEN 256
//#define FFT_LEN 512
#define FFT_LEN 1024
//#define FFT_LEN 2048
//#define FFT_LEN 4096

で指定します。

  • 現状の窓関数とFFTのオーバーラップサンプル数は、デフォルトになっており、 ハミング窓 で、Tap数/2ずつ窓をずらす仕様になっています。
    こちらを変更したい場合は、SubFFT.ino の中の

  FFT.begin();

  FFT.begin(使, , );

で変更してください。

8.1.5. TAP数変更に関する注意事項

FFTのTAP数を変更する場合、リアルタイム処理を行うためのCPUの処理性能とSubCoreのメモリ使用量に関して、以下の注意が必要です。

  • TAP数を4096にする場合

SubFFT.ino 内の

USER_HEAP_SIZE(64 * 1024);

USER_HEAP_SIZE(64 * 1024 * 5);

にしないと、4チャンネルでの動作は出来ません。

この場合、SubCore 側のメモリが不足するので、 Arduino IDE 上から ツール → Memory → 640kB を選択して MainCore のメモリサイズを640kBにする必要があります。

2チャンネルであれば、MainCoreのレイアウト変更なしで、

USER_HEAP_SIZE(64 * 1024 * 4);

に変更することで動作します。

  • TAP数を2048にする場合

SubFFT.ino 内の

USER_HEAP_SIZE(64 * 1024);

USER_HEAP_SIZE(64 * 1024 * 4);

に変更することで動作が可能です。

2チャンネルの場合、Heap変更なしで動作します。

  • TAP数を256にする場合

MainCore側の表示性能の条件から2チャンネルまでしか動作しません。

8.2. SoundDetector

8.2.1. 概要

このサンプルスケッチはマイクで入力されている音声データをFFTライブラリでリアルタイム周波数解析を行い、指定した周波数範囲内に一定以上の音圧のある音声が入力されたかどうかを検出します。Audio 機能に関しては、 Audio ライブラリ をご参照ください。

また、本サンプルは、信号処理ライブラリをSubCoreにオフロードして処理を行っております。同時に MultiCore MP ライブラリ もご参照ください。

8.2.2. 動作環境

  • Spresense メイン&拡張ボード

  • 録音用マイク

マイクの接続については、以下のハードウェアガイドを参照してください。

提供しているサンプルでは4チャンネルのマルチチャンネルでの動作になっています。
マイク数は必要にあわせて接続し、アプリケーション内で調整してください。

8.2.3. 動作手順

主な動作手順の流れは以下になります。

  1. 信号処理を行うSubCoreをコンパイル&アップロード

  2. 音声取得&アプリケーション処理を行うMainCoreをコンパイル&アップロード

  3. アプリケーションの実行

MainCoreのコンパイル&アップロードから実施すると、起動後、SubCoreへの呼び出しを実行しエラーになります。
SubCoreへのコンパイル&アップロードをすでに実行していた場合、MainCore起動後は、既にアップロードされているSubCoreのプログラムが起動されます。
8.2.3.1. SubCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → SoundDetector → SubFFT を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → SubCore1 (ツール → Core → SubCore1) を選択します

    本サンプルでは、オフロードしている SubCoreSubCore1 を使用しています。
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

    SubCore のプログラムは、 起動後、 MainCore からの指示を待つ状態になります。

これで、SubCore側の準備はOKです。

8.2.3.2. MainCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → SoundDetector → MainAudio を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → MainCore (ツール → Core → MainCore) を選択します

  3. マイコンボードへの書き込みを行います。

SubCore のアップロードでシリアルポートを選択している場合は、再度の選択は不要です。

これで、MainCore側の準備もOKです。

8.2.3.3. アプリケーションの実行
  1. MainCore のアップロードが終わったら、Tools → シリアルモニタ を開き アプリケーションを起動します。

  2. アプリケーションが起動されると、マイクから入力された各チャネルの音声を周波数解析し、指定した周波数範囲内に行って以上の音圧での音声が入力された場合、 Found channel 0 等のように、チャンネル番号と共に検出を表示します。

8.2.4. プログラム解説

  • 入力音声のチャンネル数は、MainAudio.ino の中の

/* Select mic channel number */
//const int mic_channel_num = 1;
//const int mic_channel_num = 2;
const int mic_channel_num = 4;

で指定します。

このサンプルでは、 SubCore 側にある #define MAX_CHANNEL_NUM 4 は、MainAudio.inomic_channel_num と同じ値にする必要があります。

本サンプルスケッチでの SubCore のコードは、引数なしの FFT.begin(); を呼んでおり、最大チャンネル数 = 実行チャンネル数になっています。
最大チャンネル数 ≠ 実行チャンネル数の場合は、FFT.begin(WindowHamming, 実行チャンネル数, (FFTLEN/2)); を呼んで、実行チャンネル数を指定してください。
  • FFTのTap数は、SubFFT.ino の中の

/* Select FFT length */

//#define FFT_LEN 32
//#define FFT_LEN 64
//#define FFT_LEN 128
//#define FFT_LEN 256
//#define FFT_LEN 512
#define FFT_LEN 1024
//#define FFT_LEN 2048
//#define FFT_LEN 4096

で指定します。

  • 現状の窓関数とFFTのオーバーラップサンプル数は、デフォルトになっており、 ハミング窓 で、Tap数/2ずつ窓をずらす仕様になっています。
    こちらを変更したい場合は、SubFFT.ino の中の

  FFT.begin();

  FFT.begin(使, , );

で変更してください。

  • 検出器の設定は、以下のようになっています。

#define POWER_THRESHOLD       30  // Power
#define LENGTH_THRESHOLD      30  // 20ms
#define INTERVAL_THRESHOLD    100 // 100ms

#define BOTTOM_SAMPLING_RATE  1000 // 1kHz
#define TOP_SAMPLING_RATE     1500 // 1.5kHz
  • 検出周波数範囲の下限は、BOTTOM_SAMPLING_RATE で指定します。Hz値を設定してください。

  • 検出周波数範囲の上限は、TOP_SAMPLING_RATE で指定します。Hz値を設定してください。

  • 音声検出のパワーの閾値は、POWER_THRESHOLD で指定します。

  • LENGTH_THRESHOLD 時間以上、該当周波数範囲での音声が入力され続けると、 音声が検出されたと判断します。

  • 検出された後、INTERVAL_THRESHOLD 以内に再検出された場合、前回の検出音声の一部であると判断されます。また、INTERVAL_THRESHOLD 以上離れて検出された場合は、別な音声が検出されたとして、再度通知を MainCore に上げます。

8.2.5. TAP数変更に関する注意事項

FFTのTAP数を変更する場合、リアルタイム処理を行うためのCPUの処理性能とSubCoreのメモリ使用量に関して、以下の注意が必要です。

  • TAP数を4096にする場合

SubFFT.ino 内の

USER_HEAP_SIZE(64 * 1024);

USER_HEAP_SIZE(64 * 1024 * 5);

にしないと、4チャンネルでの動作は出来ません。

この場合、SubCoreがわのメモリが不足するので、 Arduino IDE 上から ツール → Memory → 640kB を選択してMainCoreのメモリサイズを640kBにする必要があります。

2チャンネルであれば、MainCoreのレイアウト変更なしで、

USER_HEAP_SIZE(64 * 1024 * 4);

に変更することで動作します。

  • TAP数を2048にする場合

SubFFT.ino 内の

USER_HEAP_SIZE(64 * 1024);

USER_HEAP_SIZE(64 * 1024 * 4);

に変更することで動作が可能です。

2チャンネルの場合、Heap変更なしで動作します。

8.3. LowPassSound

8.3.1. 概要

このサンプルスケッチはマイクで入力されている音声データに対して、IIRによるLPF(ローパスフィルタ)をかけて出力します。Audio 機能に関しては、 Audio ライブラリ をご参照ください。

また、本サンプルは、信号処理ライブラリをSubCoreにオフロードして処理を行っております。同時に MultiCore MP ライブラリ もご参照ください。

8.3.2. 動作環境

  • Spresense メイン&拡張ボード

  • 再生用ヘッドホン or スピーカー

  • 録音用マイク

このサンプルでは、ヘッドホン、マイクを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。
マイクの接続については、以下のハードウェアガイドを参照してください。

提供しているサンプルは、モノラルマイクになっています。マイク数は必要にあわせて接続し、アプリケーション内で調整してください。
音声出力は、ステレオまでしかできません。入力を3チャンネル以上にした場合、出力までにDownMix等の処理を行い、2チャンネルデータへ変換して出力してください。

8.3.3. 動作手順

主な動作手順の流れは以下になります。

  1. 信号処理を行うSubCoreをコンパイル&アップロード

  2. 音声取得&アプリケーション処理を行うMainCoreをコンパイル&アップロード

  3. アプリケーションの実行

MainCoreのコンパイル&アップロードから実施すると、起動後、SubCoreへの呼び出しを実行しエラーになります。
SubCoreへのコンパイル&アップロードをすでに実行していた場合、MainCore起動後は、既にアップロードされているSubCoreのプログラムが起動されます。
8.3.3.1. SubCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → LowPassSound → SubIIR を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → SubCore1 (ツール → Core → SubCore1) を選択します

    本サンプルでは、オフロードしている SubCoreSubCore1 を使用しています。
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

    SubCore のプログラムは、 起動後、 MainCore からの指示を待つ状態になります。

これで、SubCore側の準備はOKです。

8.3.3.2. MainCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → LowPassSound → MainAudio を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → MainCore (ツール → Core → MainCore) を選択します

  3. マイコンボードへの書き込みを行います。

SubCore のアップロードでシリアルポートを選択している場合は、再度の選択は不要です。

これで、MainCore側の準備もOKです。

8.3.3.3. アプリケーションの実行
  1. MainCore のアップロードが終わったら、Tools → シリアルモニタ を開き アプリケーションを起動します。

  2. アプリケーションが起動されると、マイクから入力された音声データにIIRによるLPF処理を行った音声データが出力されます。

8.3.4. プログラム解説

  • 入力音声のチャンネル数は、MainAudio.ino の中の

static const int32_t channel_num  = AS_CHANNEL_MONO;
static const int32_t bit_length   = AS_BITLENGTH_16;
static const int32_t frame_sample = 768;

で指定します。このサンプルではチャンネス数を1chに、処理フレームのサンプル数を768にしています。

ビット長は16ビットのみ対応です。
チャンネル数、フレームのサンプル数は、SubCore 側と合わせる必要があります。
音声出力時には、必ずインタリーブされたステレオデータに変換する必要があります。
本サンプルでは、そのため信号処理後、モノラルデータをステレオデータに変換して出力しています。
  • カットオフ周波数とQ値は、SubCore 側の SubIIR.ino の中の

const int   g_channel = 1; /* Number of channels */
const int   g_cutoff  = 1000; /* Cutoff frequency */
const float g_Q       = sqrt(0.5); /* Q Value */

で設定しています。

本サンプルでは、カットオフ周波数を1kHz、Q値を1/√2に設定しています。
出力フレームのサンプル数は、何も指定しないとデフォルト値の768サンプルになります。
サンプリングレートは、何も指定しないとデフォルト値の48kHzになります。
出力フォーマットを指定しない場合、デフォルトのフォーマットは、チャンネルごとの出力 Planar になってます。

本サンプルでは、FrontEndObjectを通じて集音を行っております。
Spresenseのオーディオ機能は、HW制約上、入出力はすべて48kHzサンプリングか、192kHzサンプリングで行われます。
音声の信号処理を伴った入出力アプリケーション等を実現する場合、16kHzなどのサンプリングレートで内部処理を実行しようすると、 SWでのリサンプリングが2度必要になり、多くのプロセッサパワーとメモリを必要とします。
今回のIIRフィルタを用いたリアルタイム音声処理の場合、メモリが不足するため16KHzでの処理はお勧めしません。
  • 信号処理後、データを取得する際、出力フォーマットが Planar の場合、チャンネル数分だけ get を呼び出しデータを取得してください。

    for (int i = 0; i < g_channel; i++) {
      cnt = LPF.get(pDst, i);
      /* Channels must be stereo for Mixer */
      if(i == 0) {
        for(int j = 0; j < cnt; j++) {
          out_buffer[pos][j * 2]     = pDst[j];
          out_buffer[pos][j * 2 + 1] = 0;
        }
      }
    }
チャンネルごとにデータの書き出し先の領域の確保が必要です。
音声出力を行うため、本サンプルでは、インターリーブされたステレオデータに変換しています。ここでは、Rチャンネルに無音を入れて生成しています。

8.4. HighPassSound

8.4.1. 概要

このサンプルスケッチはマイクで入力されている音声データに対して、IIRによるHPF(ハイパスフィルタ)をかけて出力します。Audio 機能に関しては、 Audio ライブラリ をご参照ください。

また、本サンプルは、信号処理ライブラリをSubCoreにオフロードして処理を行っております。同時に MultiCore MP ライブラリ もご参照ください。

8.4.2. 動作環境

  • Spresense メイン&拡張ボード

  • 再生用ヘッドホン or スピーカー

  • 録音用マイク

このサンプルでは、ヘッドホン、マイクを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。
マイクの接続については、以下のハードウェアガイドを参照してください。

提供しているサンプルは、ステレオマイクになっています。マイク数は必要にあわせて接続し、アプリケーション内で調整してください。
音声出力は、ステレオまでしかできません。入力を3チャンネル以上にした場合、出力までにDownMix等の処理を行い、2チャンネルデータへ変換して出力してください。

8.4.3. 動作手順

主な動作手順の流れは以下になります。

  1. 信号処理を行うSubCoreをコンパイル&アップロード

  2. 音声取得&アプリケーション処理を行うMainCoreをコンパイル&アップロード

  3. アプリケーションの実行

MainCoreのコンパイル&アップロードから実施すると、起動後、SubCoreへの呼び出しを実行しエラーになります。
SubCoreへのコンパイル&アップロードをすでに実行していた場合、MainCore起動後は、既にアップロードされているSubCoreのプログラムが起動されます。
8.4.3.1. SubCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → HighPassSound → SubIIR を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → SubCore1 (ツール → Core → SubCore1) を選択します

    本サンプルでは、オフロードしている SubCoreSubCore1 を使用しています。
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

    SubCore のプログラムは、 起動後、 MainCore からの指示を待つ状態になります。

これで、SubCore側の準備はOKです。

8.4.3.2. MainCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → HighPassSound → MainAudio を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → MainCore (ツール → Core → MainCore) を選択します

  3. マイコンボードへの書き込みを行います。

SubCore のアップロードでシリアルポートを選択している場合は、再度の選択は不要です。

これで、MainCore側の準備もOKです。

8.4.3.3. アプリケーションの実行
  1. MainCore のアップロードが終わったら、Tools → シリアルモニタ を開き アプリケーションを起動します。

  2. アプリケーションが起動されると、マイクから入力された音声データにIIRによるHFP処理を行った音声データが出力されます。

8.4.4. プログラム解説

  • 入力音声のチャンネル数や処理フレームのサンプル数は、MainAudio.ino の中の

static const int32_t channel_num  = AS_CHANNEL_STEREO;
static const int32_t bit_length   = AS_BITLENGTH_16;
static const int32_t frame_sample = 240;

で指定します。このサンプルではチャンネル数を2chに、処理フレームのサンプル数を240にしています。

ビット長は16ビットのみ対応です。
チャンネル数、処理フレームのサンプル数は、SubCore 側と合わせる必要があります。
本サンプルでは、処理フレームのサンプル数は240になっています。
フレームサンプル数を小さくすると音声処理のレーテンシは減りますが、CPUの処理負荷は増えます。
  • カットオフ周波数とQ値は、SubCore 側の

const int   g_channel = 2; /* Number of channels */
const int   g_cutoff  = 1000; /* Cutoff frequency */
const float g_Q       = sqrt(0.5); /* Q Value */
const int   g_sample  = 240; /* Number of channels */
const int   g_fs      = 48000; /* Sampling Rate */

で設定しています。
本サンプルでは、カットオフ周波数を1kHz、Q値を1/√2に、出力フレームのサンプル数を240に、サンプリングレートを48kHzに設定しています。

また、

  if(!HPF.begin(TYPE_HPF, g_channel, g_cutoff, g_Q, g_sample, IIRClass::Interleave, g_fs)) {

で、出力フォーマットを、 Interleave に指定しています。

本サンプルでは、FrontEndObjectを通じて集音を行っております。
Spresenseのオーディオ機能は、HW制約上、入出力はすべて48kHzサンプリングか、192kHzサンプリングで行われます。
音声の信号処理を伴った入出力アプリケーション等を実現する場合、16kHzなどのサンプリングレートで内部処理を実行しようすると、SWでのリサンプリングが2度必要になり、多くのプロセッサパワーとメモリを必要とします。
今回のIIRフィルタを用いたリアルタイム音声処理の場合、メモリが不足するため16KHzでの処理はお勧めしません。

8.5. VoiceChanger

8.5.1. 概要

このサンプルスケッチはマイクで入力されている音声データを、FFT/iFFTを行い音程を変更し音声データを再生成します。生成したデータに位相情報の欠落などにより付加されている高周波ノイズをカットするためにIIRによるLPFフィルタを掛けて出力します。Audio 機能に関しては、 Audio ライブラリ をご参照ください。

また、本サンプルは、信号処理ライブラリをSubCoreにオフロードして処理を行っております。同時に MultiCore MP ライブラリ もご参照ください。

本サンプルは、あくまで単純なFFT/iFFTの実行サンプルとしてご提供しています。そのため、ピッチシフタとしての音声品質に関しては言及しておりません。ご了承ください。

8.5.2. 動作環境

  • Spresense メイン&拡張ボード

  • 再生用ヘッドホン or スピーカー

  • 録音用マイク

このサンプルでは、ヘッドホン、マイクを使用します。 拡張ボードのヘッドホン端子にヘッドホンもしくはアクティブスピーカーを接続してください。
マイクの接続については、以下のハードウェアガイドを参照してください。

提供しているサンプルは、モノラルマイクになっています。

8.5.3. 動作手順

主な動作手順の流れは以下になります。

  1. 信号処理を行うSubCoreをコンパイル&アップロード

  2. 音声取得&アプリケーション処理を行うMainCoreをコンパイル&アップロード

  3. アプリケーションの実行

MainCoreのコンパイル&アップロードから実施すると、起動後、SubCoreへの呼び出しを実行しエラーになります。
SubCoreへのコンパイル&アップロードをすでに実行していた場合、MainCore起動後は、既にアップロードされているSubCoreのプログラムが起動されます。
8.5.3.1. SubCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → VoiceChanger → SubFFT を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → SubCore1 (ツール → Core → SubCore1) を選択します

    本サンプルでは、オフロードしている SubCoreSubCore1 を使用しています。
  3. ツール → シリアルポート で Spresense の COM ポートを選択して、マイコンボードへの書き込みを行います。

    SubCore のプログラムは、 起動後、 MainCore からの指示を待つ状態になります。

これで、SubCore側の準備はOKです。

8.5.3.2. MainCoreのコンパイル&アップロード
  1. Arduino IDE 上から ファイル → スケッチ例 → Spresense用のスケッチ例 SignalProcessing → VoiceChanger → MainAudio を選択してサンプルスケッチを開きます。

  2. Arduino IDE メニューから Tools → Core → MainCore (ツール → Core → MainCore) を選択します

  3. マイコンボードへの書き込みを行います。

SubCore のアップロードでシリアルポートを選択している場合は、再度の選択は不要です。

これで、MainCore側の準備もOKです。

8.5.3.3. アプリケーションの実行
  1. MainCore のアップロードが終わったら、Tools → シリアルモニタ を開き アプリケーションを起動します。

  2. FFTにより得られた周波数領域でのデータを加工し、音程を変更したデータをiFFTで再構成し、LPFを掛けて出力されます。

8.5.4. プログラム解説

  • 入力音声のチャンネル数や処理フレームのサンプル数は、MainAudio.ino の中の

static const int32_t channel_num  = AS_CHANNEL_MONO;
static const int32_t bit_length   = AS_BITLENGTH_16;
static const int32_t frame_sample = 1024;

で指定します。

ビット長は16ビットのみ対応です。
チャンネル数、処理フレームのサンプル数は、SubCore 側と合わせる必要があります。
本サンプルでは、処理フレームのサンプル数は1024になっています。
FFTのTAP数をベースにパイプラインの構成に合わせて指定してください。
音声出力時には、必ずインタリーブされたステレオデータに変換する必要があります。
本サンプルでは、そのため信号処理後、モノラルデータをステレオデータに変換して出力しています。
  • 音声ピッチのシフト量は、MainAudio.ino の中の

static int pitch_shift = -10;

で指定しています。FFTの分解能に合わせて、正負方向にシフトできます。
本サンプルでは、48kHzサンプリングの音声データに1024TAPのFFT/iFFT処理を行っているので、1シフトで約46Hz変更されます。
このパラメータは、信号処理動作中でも変更が可能です。

  • FFTのTap数は、SubFFT.ino の中の

/* Select FFT length */

//#define FFT_LEN 32
//#define FFT_LEN 64
//#define FFT_LEN 128
//#define FFT_LEN 256
//#define FFT_LEN 512
#define FFT_LEN 1024
//#define FFT_LEN 2048
//#define FFT_LEN 4096

で指定します。

  • 本サンプルは、単純にFFT/iFFTで音声を再生成しますので、矩形窓 で、オーバーラップなしになっています。

   FFT.begin(WindowRectangle,MAX_CHANNEL_NUM,0);

アルゴリズム等変更の場合は、合わせて変更してください。

  • iFFT後のLPFの設定値は、

const int   g_channel = MAX_CHANNEL_NUM; /* Number of channels */
const int   g_cutoff  = 1000; /* Cutoff frequency */
const float g_Q       = sqrt(0.5); /* Q Value */
const int   g_sample  = 1024; /* Number of channels */

const int   g_max_shift = 30; /* Pitch shift value */

としています。カットオフ周波数を1kHz、Q値を1/√2にしていますが、適宜変更してください。

モノラル入力なので、出力フォーマットはデフォルトの Planar になっています。
  • シフト量の最大値を

const int   g_max_shift = 30; /* Pitch shift value */

で指定しています。本サンプルは30としています。

  • 出力音声データはこちらでインターリーブされたステレオデータに変換しています。

        for(int j = 0; j < cnt; j++) {
          pOut[pos][j * 2]     = pLpfDst[j];
          pOut[pos][j * 2 + 1] = 0;
        }

ここでは、Rチャンネルに無音を入れて生成しています。

SpresenseのAudio処理内部は、HW制約上入出力はすべて48kHzサンプリングか、192kHzサンプリングで行われます。
16kHzなどのサンプリングレートで内部処理を実行しようとした場合、SWでのリサンプリングが2度必要になり、 多くのプロセッサパワーとメモリを必要とします。今回のVoiceChangerの場合、メモリが不足するため16KHzでの処理は行えません。
ご注意ください。