Developer World Spresense
English 中文
目次

1. Examples 一覧

Spresense SDKでは、NuttShell の Built-in コマンドとして各種 Example を用意しています。

SDK v2.0 以降から NuttX オリジナルのアプリケーションも Examples に追加されています。

(SDK v1.x 以前のバージョンのチュートリアルは こちら を参照してください)

初めてビルドする方は、はじめにスタートガイドを参照してください。 コンフィグレーションの方法や、ビルドしたプログラムを実行する手順についての詳しい説明があります。

1.1. SDK examples

カテゴリ Example名 説明

Peripheral
Driver

adc_monitor

A/Dコンバータを使用してアナログ入力値を表示するサンプルです。

GPS (GNSS)

gnss

GNSS機能を用いて位置情報を取得するサンプルです。

geofence

GNSS Geofence機能を使用したサンプルです。

gnss_atcmd

GNSS ATコマンドを使用したサンプルです。ターミナル上にNMEAセンテンスを表示します。

gnss_factory

GNSS 評価用のサンプルです。

gnss_pvtlog

GNSS PVTログを出力するサンプルです。

gnss_addon

GNSS Add-onボードを使用して位置情報を取得するサンプルです。

AudioLite

audiolite_mp3player

MP3プレーヤーのサンプルです。

audiolite_wavplayer

WAVプレーヤーのサンプルです。

audiolite_wavrecorder

WAVレコーダーサンプルです。

Audio

audio_player

オーディオプレーヤーのサンプルです。

audio_recorder

オーディオレコーダーのサンプルです。

audio_through

オーディオのパススルー機能を用いて、マイク入力、スピーカ出力、I2S入出力を使用したサンプルです。

audio_pcm_capture

PCMデータをキャプチャするサンプルです。

audio_recognizer

音声認識フレームワークのサンプルです。

audio_beep

オーディオビープ音を再生するサンプルです。

audio_dual_players

オーディオのデュアルデコード再生のサンプルです。

audio_player_objif

オブジェクトインターフェース層を用いたオーディオプレーヤーのサンプルです。

audio_recorder_objif

オブジェクトインターフェース層を用いたオーディオレコーダーのサンプルです。

audio_pcm_capture_objif

オブジェクトインターフェース層を用いたPCMキャプチャのサンプルです。

audio_sound_effector

低遅延で音声エフェクトを行うサンプルです。

ASMP

asmp

ASMPフレームワークによるマルチコアを使用したサンプルです。

prime

マルチコアを使用して素数計算を行うサンプルです。

fft

マルチコアを使用してFFT演算を行うサンプルです。

Sensor

accel

加速度センサからセンサ情報を取得するサンプルです。

gyro

ジャイロセンサからセンサ情報を取得するサンプルです。

light

照度センサからセンサ情報を取得するサンプルです。

mag

地磁気センサからセンサ情報を取得するサンプルです。

press

気圧センサからセンサ情報を取得するサンプルです。

proximity

近接センサからセンサ情報を取得するサンプルです。

colorsensor

カラーセンサからセンサ情報を取得するサンプルです。

tilt

加速度センサを使用して傾き検出を行うサンプルです。

decimator

SCUによるデシメータ機能を使って間引き処理を行うサンプルです。

step_counter

加速度センサによる歩数計及び行動認識を行うサンプルです。

Camera

multi_webcamera

マルチウェブカメラアプリケーションのサンプルです。

JPEG

jpeg_decode

JPEGデコードのサンプルです。

DNN

dnnrt_lenet

DNN Runtime機能を使用して、数字認識を行うサンプルです。

LTE

lte_http_get

LTE通信機能を用いて、HTTP GETを行うサンプルです。

lte_tls

LTE通信機能を用いて、TLS通信を行うサンプルです。

lte_mqtt

LTE通信機能を用いて、MQTT通信を行うサンプルです。

lte_lwm2m

LTE通信機能を用いて、Lightweight M2M(LWM2M)通信を行うサンプルです。

lte_awsiot

LTE通信機能を用いて、AWS IoTと接続するサンプルです。

lte_azureiot

LTE通信機能を用いて、Azure Iot Hubと接続するサンプルです。

sms_send

SMS(Short Message Service) の送信を行うサンプルです。

sms_recv

SMS(Short Message Service) の受信を行うサンプルです。

DigitalFilter

digital_filter

DigitalFilterライブラリを用いたFIRフィルタに関するサンプルです。

HostIF

hostif

HostIF機能を使用して外部HostとI2C, SPI通信を行うサンプルです。

FW Update

fwupdate

アプリケーションからファームウェアアップデートを行うサンプルです。

ELTRES

eltres_lpwa

ELTRES LPWA通信を行うサンプルです。

eltres_standalone

ELTRES スタンドアローンモードで動作するサンプルです。

eltres_eeprom

ELTRES EEPROMを読み書きするサンプルです。

FileSystem

fsperf

ファイルシステムのパフォーマンスを測定するサンプルです。

Others

setjmp

setjmp()/longjmp() 関数を使用したサンプルです。

1.2. NuttX examples

カテゴリ Example名 説明

Hello

hello

C言語による"Hello, World"を出力するサンプルです。

helloxx

C++言語による"Hello, World"を出力するサンプルです。

Peripheral
Driver

alarm

RTC(リアルタイムクロック)によるアラーム機能を使用したサンプルです。

watchdog

WDTウォッチドッグタイマーを使用したサンプルです。

pwm

PWM出力を行うサンプルです。

Camera

camera

カメラ機能のサンプルです。

Graphics

nx

NX graphics機能を用いて、描画するサンプルです。

nxhello

NX graphics機能を用いて、"Hello"を描画するサンプルです。

nximage

NX graphics機能を用いて、ビットマップを描画するサンプルです。

nxlines

NX graphics機能を用いて、線を描画するサンプルです。

nxtext

NX graphics機能を用いて、テキストを描画するサンプルです。

Network

ftpc

FTP 転送(Client)を行うサンプルです。

ftpd

FTP 転送(Server)を行うサンプルです。

tcpecho

TCP 通信でエコーを行うサンプルです。

tcpblaster

Server/Client 間で TCP 通信を行うサンプルです。

Sensor

bmi160

BMI160加速度/ジャイロの6軸センサを使用したサンプルです。

Battery

charger

充電機能のサンプルです。(Spresenseボードでは充電機能はサポートされていません)

Others

json

cJSON ライブラリを用いた JSON パーサーのサンプルです。

ini_dumper

inih ライブラリを用いて ini ファイルをパースするサンプルです。

pdcurses

PDCurses 機能を動かすサンプルです。

embedlog

embedlog ライブラリを用てロギングを行うサンプルです。

2. Peripheral Driver チュートリアル

2.1. RTC alarm サンプルアプリケーション

RTC alarm サンプルアプリケーションの動作について説明します。

2.1.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/alarm を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/alarm
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

2.1.2. 動作確認

シリアルターミナルを開いて、alarm コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から alarm コマンドを実行します。

    alarm コマンドの使い方を以下に示します。

    nsh> alarm
    ERROR: Invalid number of arguments: 0
    USAGE:
            alarm <seconds>
    Where:
            <seconds>
                    The number of seconds until the alarm expires.

    <seconds> には相対時間(秒)を指定します。例えば、alarm 5 と入力すると、5 秒後にアラームが発火します。

    nsh> alarm 5
    alarm_daemon started
    alarm_daemon: Running
    Opening /dev/rtc0
    Alarm 0 set in 5 seconds
    nsh> alarm_demon: alarm 0 received

2.1.3. 省電力機能と組み合わせて使用する

alarm コマンドと省電力機能を組み合わせて使用する例について紹介します。

Spresense には、Deep Sleep や Cold Sleep モードといった省電力機能が提供されています。 poweroff コマンドを用いてこれらの Sleep 状態に入ることができます。 そして、RTC アラーム機能により、この Sleep 状態から起床することができます

Deep Sleep や Cold Sleep について、詳しくは スリープモード を参照してください。

2.1.3.1. Deep Sleep モードからの起床

以下の例では、10 秒後にアラームを設定してから poweroff コマンドにより Deep Sleep モードに入ります。
10 秒後にアラームが発火して Deep Sleep 状態から起床します。

nsh> alarm 10
alarm_daemon started
alarm_daemon: Running
Opening /dev/rtc0
Alarm 0 set in 10 seconds
nsh> poweroff

NuttShell (NSH) NuttX-8.2
nsh>
2.1.3.2. Cold Sleep モードからの起床

以下の例では、10 秒後にアラームを設定しておいて、poweroff 1 により Cold Sleep モードに入ります。
10 秒後にアラームが発火して Cold Sleep 状態から起床します。

nsh> alarm 10
alarm_daemon started
alarm_daemon: Running
Opening /dev/rtc0
Alarm 0 set in 10 seconds
nsh> poweroff 1

NuttShell (NSH) NuttX-8.2
nsh>

2.1.4. その他の RTC に関するコマンド

date コマンドにより、RTC に時刻を設定したり、RTC に設定された現在時刻を表示することができます。

nsh> help date
date usage:  date [-s "MMM DD HH:MM:SS YYYY"]

例)RTC に 2019年12月1日23時34分56秒を設定する場合、

nsh> date -s "Dec 1 23:34:56 2019"

date コマンドにより現在時刻を表示します。

nsh> date
Dec 01 23:35:14 2019

RTC は、Deep/Cold Sleep といったスリープ中や reboot コマンドにより再起動した場合でも時刻を保持し続けます。ただし、電源供給がオフされたり、リセットボタンが押された場合は、RTC に設定された時刻はリセットされます。

以下に reboot コマンドによる再起動後も時刻が保持されている例を示します。

nsh> date
Dec 01 23:41:08 2019
nsh> reboot

NuttShell (NSH) NuttX-8.2
nsh> date
Dec 01 23:41:12 2019
nsh>

2.2. Watchdog サンプルアプリケーション

Watchdog サンプルアプリケーションの動作について説明します。

2.2.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/watchdog を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/watchdog
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

2.2.2. 動作確認

シリアルターミナルを開いて、wdog コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から wdog コマンドを実行します。

    wdog コマンドの使い方を以下に示します。

    nsh> wdog -h
    Usage: wdog [-h] [-d <pingdelay>] [-p <pingtime>] [-t <timeout>]
    
    Initialize the watchdog to the <timeout>. Start the watchdog
    timer. Ping for the watchdog for <pingtime> seconds, then let it expire.
    
    Options include:
      [-d <pingdelay>] = Time delay between pings in milliseconds. Default: 500
      [-p <pingtime>] = Selects the <pingtime> time in milliseconds. Default: 5000
      [-t timeout] = Time in milliseconds that the example will ping the watchdog
        before letting the watchdog expire. Default: 2000
      [-h] = Shows this message and exits
    -d

    指定された周期 [msec] で ioctl(fd, WDIOC_KEEPALIVE, 0) を呼び出すことで watchdog タイマーをクリアします。

    -p

    指定された期間中 [msec] は watchdog をクリアし続けます。

    -t

    指定された値 [msec] で ioctl(fd, WDIOC_SETTIMEOUT, (unsigned long)wdog.timeout) を呼び出しwatchdog タイマーの周期を設定します。

本サンプルアプリケーションは、watchdog タイマーの発火に伴いシステムがリブートすることを確認できます。

wdog コマンドを実行した例を以下に示します。 引数無しで動作させたときは、デフォルト値の 2 秒で watchdog タイマー周期を設定します。 5 秒間は 500 msec 周期で watchdog タイマーをクリアし続けます。 その後、watchdog タイマーをクリアしないままタイマーが満了してシステムがリブートします。

nsh> wdog
  ping elapsed=0
  ping elapsed=500
  ping elapsed=1000
  ping elapsed=1500
  ping elapsed=2000
  ping elapsed=2500
  ping elapsed=3000
  ping elapsed=3500
  ping elapsed=4000
  ping elapsed=4500
  NO ping elapsed=5000
  NO ping elapsed=5500
  NO ping elapsed=6000
up_assert: Assertion failed at file:irq/irq_unexpectedisr.c line: 65 task: Idle Task
up_dumpstate: sp:     0d0279d4
up_dumpstate: IRQ stack:
up_dumpstate:   base: 0d027a00
up_dumpstate:   size: 00000800
up_dumpstate:   used: 00000120
up_stackdump: 0d0279c0: 00000000 0d003e3d 0d0291a8 0d02975c 00000000 00000002 466cc9d4 0d002f69
up_stackdump: 0d0279e0: 0d002f55 0d00703d 00000000 0d02975c 0d028a20 00000003 00000000 0d006fd5
up_dumpstate: sp:     0d029830
up_dumpstate: User stack:
up_dumpstate:   base: 0d029840
up_dumpstate:   size: 00000400
up_dumpstate:   used: 00000000
up_stackdump: 0d029820: 9b7feebc 1f86add5 00000000 0d002e75 0d029844 001567bc 2df7cabf 00000000
up_registerdump: R0: 00000000 0d026bcc 0d02df68 00000014 0d026b54 0d028a20 00000003 00000000
up_registerdump: R8: 0d026ca0 f0bbaf7f dc9161d8 466cc9d4 00000003 0d029830 0d002e79 0d008792
up_registerdump: xPSR: 21000000 BASEPRI: 00000000 CONTROL: 00000000
up_registerdump: EXC_RETURN: ffffffe9
up_taskdump: Idle Task: PID=0 Stack Used=0 of 0
up_taskdump: hpwork: PID=1 Stack Used=344 of 2028
up_taskdump: lpwork: PID=2 Stack Used=352 of 2028
up_taskdump: lpwork: PID=3 Stack Used=352 of 2028
up_taskdump: lpwork: PID=4 Stack Used=352 of 2028
up_taskdump: init: PID=5 Stack Used=1032 of 8172
up_taskdump: cxd56_pm_task: PID=6 Stack Used=320 of 996
up_taskdump: wdog: PID=8 Stack Used=528 of 2028

NuttShell (NSH) NuttX-8.2
nsh>

2.3. ADC サンプルアプリケーション

この章では、ADC サンプルアプリケーションの動作手順を示します。

2.3.1. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/adc_monitor を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/adc_monitor
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

2.3.2. 動作確認

シリアルターミナルを開いて、adc_monitor コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から adc_monitor コマンドを実行します。

    adc_monitor コマンドの Usage を以下に示します。

    nsh> adc_monitor -h
    Usage: adc_monitor [OPTIONS]
    
    Arguments are "sticky".  For example, once the ADC device is
    specified, that device will be re-used until it is changed.
    
    "sticky" OPTIONS include:
      [-p devpath] selects the ADC device.  /dev/lpadc0, 1, 2, 3, /dev/hpadc0, 1  Current: /dev/lpadc0
      [-n count] set the number of reads.  Current: 10
      [-h] shows this message and exits
    -p

    全部で 6 つの ADC 専用端子があります。-p オプションにより、-p /dev/lpadc[0-3] もしくは /dev/hpadc[0-1] を指定します。 Spresense ボード上のピン番号とデバイスファイルの関係は下記の通りです。

    ピン番号

    A0

    A1

    A2

    A3

    A4

    A5

    /devファイル

    /dev/lpadc0

    /dev/lpadc1

    /dev/lpadc2

    /dev/lpadc3

    /dev/hpadc0

    /dev/hpadc1

    -n

    測定回数を指定します。

例えば、HPADC0 (A4) に対して、10 回分の ADC データ取得を行います。 HPADC0 に対して、ADC データをバッファリングし、その平均値、最小値、最大値を表示します。 ADC データは 16bit の符号付きデータで、範囲は -32767 ~ 32767 です。

nsh> adc_monitor -p /dev/hpadc0 -n 10
ADC example - Name:/dev/hpadc0 bufsize:16
Ave:-32767 Min:-32767 Max:-32767 Cnt:8
Ave:-32767 Min:-32767 Max:-32767 Cnt:8
Ave:-32767 Min:-32767 Max:-32767 Cnt:8
Ave:14673 Min:14668 Max:14676 Cnt:8
Ave:14681 Min:14677 Max:14684 Cnt:8
Ave:14690 Min:14687 Max:14694 Cnt:8
Ave:14684 Min:14680 Max:14690 Cnt:8
Ave:14677 Min:14672 Max:14682 Cnt:8
Ave:14677 Min:14675 Max:14682 Cnt:8
Ave:14672 Min:14667 Max:14677 Cnt:8
ADC example end

2.3.3. ADC サンプリング周波数について

ADC のサンプリング周波数は SCU clock mode により選択されたクロックに依存します。

tutorial scu clock
2.3.3.1. HPADC (High Performance ADC)

HPADC は高速サンプリングが可能な ADC です。

HPADC の クロック系統図を以下に示します。

Diagram

クロックソースを固定分周して HPADC クロックが決まります。 その ADC クロックを 2 のべき乗で分周してサンプリング周波数が決定されます。

n の値は、次の SDK コンフィグレーションにより変更することができます。

System Type -> CXD56xx Package Configuration -> Peripheral Support ->
  ADC -> HPADC0 -> Coefficient of sampling frequency (CONFIG_CXD56_HPADC0_FREQ)
  ADC -> HPADC1 -> Coefficient of sampling frequency (CONFIG_CXD56_HPADC1_FREQ)
tutorial hpadc coef

n の取りうる範囲は、SCU clock mode によって変わります。

  1. SCU clock mode = RTC の場合

    n 9 10 11

    Fs(Hz)

    64

    32

    16

    Available

  2. SCU clock mode = RCOSC/XOSC の場合

    n 0 2 3 4 5 6 7(*1)

    Fs(Hz)

    540-550K(*3)

    512K

    256K

    128K

    64K

    32K

    16K

    Available

    △(*4)

    △(*2)

    △(*2)

    △(*2)

    (*1): デフォルトは SCU clock mode = RCOSC, n = 7 が選択されており、Fs = 16KHz です。
    (*2): CONFIG_CXD56_HPADC0_HIGHSPEED=y のとき、最大512KHzまでのFsをサポートします。
    (*3): CONFIG_CXD56_HPADC0_HIGHSPEED=y のとき、SCUシーケンサの実行性能に律速し、540〜550KHzで動作します。
    (*4): n = 0 にしたとき、後段の平滑化CICフィルタが無効になり、10bit精度のADC生値を出力します。

    CONFIG_CXD56_HPADC0_HIGHSPEED を有効にした場合、
    HPADC1、LPADC、I2C/SPI の SCUシーケンサを使用することはできなくなります。

    高速サンプリングレート(512KHz)を使用する際の推奨コンフィグレーションを以下に示します。

    Configuration Value Description

    CONFIG_CXD56_ADC

    y

    ADC を有効にします。

    CONFIG_CXD56_HPADC0

    y

    HPADC0 を有効にします。

    CONFIG_CXD56_HPADC1

    n

    HPADC1 を無効にします。

    CONFIG_CXD56_LPADC

    n

    LPADC を無効にします。

    CONFIG_CXD56_HPADC0_FREQ

    2

    Fsを512KHzに設定します。

    CONFIG_CXD56_HPADC0_HIGHSPEED

    y

    高速オプションを有効にします。

    CONFIG_CXD56_HPADC0_INPUT_GAIN_M6DB

    y

    入力ゲイン設定として-6dBを選択します。

    CONFIG_CXD56_I2C0_SCUSEQ

    n

    I2C0 SCUシーケンサを無効にします。

    CONFIG_CXD56_I2C1_SCUSEQ

    n

    I2C1 SCUシーケンサを無効にします。

    CONFIG_CXD56_SPI3_SCUSEQ

    n

    SPI3 SCUシーケンサを無効にします。

    CONFIG_CXD56_SCU_XOSC

    y

    SCUクロック源としてXOSCを選択します。

2.3.3.2. LPADC (Low Power ADC)

LPADC は HPADC に比べてサンプリングレートは低速ですが省電力で動作する ADC です。 LPADC のクロック系統図を以下に示します。

Diagram

LPADC を RTC クロックをベースに動作します。そのクロックを 2 のべき乗で分周してサンプリング周波数が決定されます。

n の値は、次の SDK コンフィグレーションにより変更することができます。

System Type -> CXD56xx Package Configuration -> Peripheral Support ->
  ADC -> LPADC0 -> Coefficient of sampling frequency (CONFIG_CXD56_LPADC0_FREQ)
  ADC -> LPADC1 -> Coefficient of sampling frequency (CONFIG_CXD56_LPADC1_FREQ)
  ADC -> LPADC2 -> Coefficient of sampling frequency (CONFIG_CXD56_LPADC2_FREQ)
  ADC -> LPADC3 -> Coefficient of sampling frequency (CONFIG_CXD56_LPADC3_FREQ)
tutorial lpadc coef

n の取りうる範囲は、SCU clock mode によって変わります。 また、LPADC は全部で 4 チャンネルありますが、1 ch のみ使用する場合、2ch で使用する場合、4ch で使用する場合 でもサンプリング周波数の上限値が変わります。それぞれのケースで、n の取りうる値を以下に示します。

tutorial lpadc ch
  1. SCU clock mode = RTC の場合

    1. LPADC channel 0 ~ 3 のいずれか一つのチャンネルを選択した場合

      n 11 12 13 14 15

      Fs(Hz)

      16

      8

      4

      2

      1

      Available

    2. LPADC channel 0 and 1 の二つのチャンネルを選択した場合

      n 12 13 14 15

      Fs(Hz)

      4

      2

      1

      0.5

      Available

    3. LPADC channel 0 ~ 3 の四つのチャンネルを選択した場合

      n 11 12 13 14 15

      Fs(Hz)

      4

      2

      1

      0.5

      0.25

      Available

  2. SCU clock mode = RCOSC の場合

    1. LPADC channel 0 ~ 3 のいずれか一つのチャンネルを選択した場合

      n 3 4 5 6 7 8 9 10 11 12 13 14 15

      Fs(Hz)

      4K

      2K

      1K

      512

      256

      128

      64

      32

      16

      8

      4

      2

      1

      Available

    2. LPADC channel 0 and 1 の二つのチャンネルを選択した場合

      n 6 7 8 9 10 11 12 13 14 15

      Fs(Hz)

      256

      128

      64

      32

      16

      8

      4

      2

      1

      0.5

      Available

    3. LPADC channel 0 ~ 3 の四つのチャンネルを選択した場合

      n 7(*) 8 9 10 11 12 13 14 15

      Fs(Hz)

      64

      32

      16

      8

      4

      2

      1

      0.5

      0.25

      Available

      (*): デフォルトは LPADC 全チャンネル有効で SCU clock mode = RCOSC, n = 7 が選択されています。

  3. SCU clock mode = XOSC の場合

    1. LPADC channel 0 ~ 3 のいずれか一つのチャンネルを選択した場合

      n 2 3 4 5 6 7 8 9 10 11 12 13 14 15

      Fs(Hz)

      8K

      4K

      2K

      1K

      512

      256

      128

      64

      32

      16

      8

      4

      2

      1

      Available

    2. LPADC channel 0 and 1 の二つのチャンネルを選択した場合

      n 6 7 8 9 10 11 12 13 14 15

      Fs(Hz)

      256

      128

      64

      32

      16

      8

      4

      2

      1

      0.5

      Available

    3. LPADC channel 0 ~ 3 の四つのチャンネルを選択した場合

      n 7 8 9 10 11 12 13 14 15

      Fs(Hz)

      64

      32

      16

      8

      4

      2

      1

      0.5

      0.25

      Available

2.4. PWM サンプルアプリケーション

PWM サンプルアプリケーションの動作について説明します。

2.4.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/pwm を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/pwm
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

2.4.2. 動作確認

シリアルターミナルを開いて、pwm コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から pwm コマンドを実行します。

    pwm コマンドの使い方を以下に示します。

    nsh> pwm -h
    Usage: pwm [OPTIONS]
    
    Arguments are "sticky".  For example, once the PWM frequency is
    specified, that frequency will be re-used until it is changed.
    
    "sticky" OPTIONS include:
      [-p devpath] selects the PWM device.  Default: /dev/pwm0 Current: /dev/pwm0
      [-f frequency] selects the pulse frequency.  Default: 1000 Hz Current: 1000 Hz
      [-d duty] selects the pulse duty as a percentage.  Default: 50 % Current: 50 %
      [-t duration] is the duration of the pulse train in seconds.  Default: 5 Current: 5
      [-h] shows this message and exits

    例)PWM1 に対して、周期 2000 Hz、デューティー比 30 % の PWM 信号を 10 秒間出力します。

    nsh> pwm -p /dev/pwm1 -f 2000 -d 30 -t 10
    -p

    全部で 4 つの PWM 専用端子があります。-p オプションにより、-p /dev/pwm[0-3] を指定します。

    -f

    PWM 周期 [Hz] を設定します。

    -d

    デューティー比(周期に対する High 期間の割り合い) [%] は、1 ~ 99 までの数字を指定します。

    -t

    指定された時間 [s] の PWM 信号を出力します。

2.4.3. PWM 周波数とデューティー比について

PWM は SCU clock mode により選択されたクロックで動作します。

tutorial scu clock

SCU clock を以下に示します。

  • Same with SCU32K → RTC 32.768kHz

  • RCOSC → 約8.2MHz

  • XOSC → TCXO 26MHz を CONFIG_CXD56_SCU_XOSC_DIV(=2) で分周した 13MHz

PWM 信号の波形は、下図で表されるように SCU clock の PWM_CYCLE カウント数により周期が決まり、 PWM_THRESH カウント数により Low 出力の期間が決定されます。カウント数の上限は 0xffff です。

tutorial pwm

PWM で設定できる周波数の範囲は、

1 <= PWM frequency <= SCU clock / 2

となり、例えば、SCU clock が RCOSC を選択した場合は、1Hz ~ 約 4MHz までとなります。

デューティー比について、-d で指定された数字から計算上の近似値で Low, High の区間を決定するため、 出力される波形は正確なデューティー比にならず丸め誤差を含むことがあります。

3. GPS チュートリアル

3.1. GNSS サンプルアプリケーション

GNSS サンプルアプリケーションの動作について説明します。

3.1.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/gnss を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/gnss
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

3.1.2. 動作確認

シリアルターミナルを開いて、gnss コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から gnss コマンドを実行します。

    tutorial gnss log1
    図 1. gnssコマンド起動時ログ

本アプリケーションを実行すると、はじめに /dev/gps ドライバを open() します。

  fd = open("/dev/gps", O_RDONLY);

openしたドライバに対して ioctl() を用いて、測定周期の設定や衛星システムの選択を行います。

  ret = ioctl(fd, CXD56_GNSS_IOCTL_SET_OPE_MODE, (uint32_t)&set_opemode);
  ret = ioctl(fd, CXD56_GNSS_IOCTL_SELECT_SATELLITE_SYSTEM, set_satellite);

ioctl() の CXD56_GNSS_IOCTL_START コマンドにより測位を開始します。

  ret = ioctl(fd, CXD56_GNSS_IOCTL_START, CXD56_GNSS_STMOD_HOT);

測位結果は、read() を用いて読みだすことができます。

  ret = read(fd, &posdat, sizeof(posdat));

本アプリケーションでは 1 秒周期で測位結果を表示します。
位置情報が取得できるまでの間は、"No Positioning Data"と表示されます。

Hour:0, minute:0, sec:3, usec:503
No Positioning Data

はじめに時刻情報を取得できたら、ターミナル上に UTC 時刻を表示します。
さらに位置情報を取得できたら次のように緯度、経度情報がターミナル上に表示されます。

Hour:9, minute:13, sec:20, usec:559
LAT 35.25.6303
LNG 139.22.1986

本アプリケーションは、位置情報を取得してから約200秒後に測位動作を終了します。
ioctl() の CXD56_GNSS_IOCTL_STOP コマンドにより測位を停止した後に、/dev/gps ドライバを close() します。

  ret = ioctl(fd, CXD56_GNSS_IOCTL_STOP, 0);
  ret = close(fd);

上空がよく見える屋外など受信環境が良い場合は、約30秒~1分ぐらいで測位ができますが、 受信環境がよくない場合は測位するまでに数分かかる場合があります。

3.1.3. 参考

GNSS 機能の詳細は、開発ガイドを参照してください。

3.2. Geofence サンプルアプリケーション

Geofence サンプルアプリケーションの動作について説明します。

Geofence は、GNSS で取得される位置情報を使って、特定のリージョンに入る・抜ける・一定期間滞在している、 といったイベントを検出しアプリケーションに通知する機能を提供します。そのリージョンは、中心座標(緯度経度)とその半径によって 指定され、ユーザーが任意の位置を最大 20 地点まで登録することができます。

3.2.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/geofence を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/geofence
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

3.2.2. 動作確認

シリアルターミナルを開いて、geofence コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から geofence コマンドを実行します。

    nsh> geofence

本アプリケーションでは、まず現在位置の情報を測位によって取得します。

その現在位置と、そこを起点として東西南北方向にそれぞれ 50 m ずつ離れた場所の計 5 つのリージョンを登録します。

現在位置の測位が完了しリージョンが設定された後に、デバイスを持ち歩いて移動することにより、 特定のリージョンに入った"ENTER"/抜けた"EXIT"/滞在している"DWELL"、といったステータスの情報がログ上に表示されます。

具体的な Geofence 機能の使い方については以下の通りです。

/dev/gps ドライバを open() して測位を行う方法は、 前述した GNSS サンプルアプリケーション を参照してください。

Geofence 機能を使用するためには /dev/geofence ドライバを open() します。

  g_fdgeo = open("/dev/geofence", O_RDONLY);

openしたドライバに対して ioctl() を用いて、Dead zone [m] や滞在通知期間 [s] を設定します。

  mode.deadzone         = 5;
  mode.dwell_detecttime = 10;
  ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_SET_MODE, (unsigned long)&mode);

ioctl() の CXD56_GEOFENCE_IOCTL_ADD コマンドにより、リージョンを追加します。

  region_xxx.id        = 0;
  region_xxx.latitude  = own_latitude;
  region_xxx.longitude = own_longitude;
  region_xxx.radius    = GEOFENE_REGION_RADIUS;

  ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_ADD, (unsigned long)&region_xxx);

ioctl() の CXD56_GEOFENCE_IOCTL_START コマンドにより、Geofence 動作を開始します。

  ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_START, 0);

Geofence イベントを poll() によって待ち受け、read() により情報を取得します。

  ret = read(g_fdgeo, &g_geofence_status, sizeof(struct cxd56_geofence_status_s));

本アプリケーションは、トータル 10 個のイベント通知がきたら Geofence 動作を終了します。
ioctl() の CXD56_GEOFENCE_IOCTL_STOP コマンドにより停止した後に、/dev/geofence ドライバを close() します。

  ret = ioctl(g_fdgeo, CXD56_GEOFENCE_IOCTL_STOP, 0);
  ret = close(g_fdgeo);

3.2.3. 参考

Geofence 機能の詳細は、開発ガイドを参照してください。

3.3. GNSS ATCMD アプリケーション

gnss_atcmd は Spresense SDK 上で動作する GNSS 機能評価用のサンプルアプリケーションです。 PC 等のホストからシリアルポートを介してコマンドを送信すると、そのシリアルポートに NMEA センテンスの結果が出力されます。具体的なコマンドの仕様とアプリケーションの使用例について示します。

3.3.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    本アプリケーションを実行するためには、以下の SDK Configuration を有効にしてください。

    CONFIG_CXD56_GNSS=y
    CONFIG_GPSUTILS_CXD56NMEA_LIB=y
    CONFIG_EXAMPLES_GNSS_ATCMD=y

    下記のコマンド実行することでこれらの Configuration を自動的に有効にすることができます。

    ./tools/config.py examples/gnss_atcmd
    

    また、コマンド入出力に使用するポートを、コンフィグレーション GNSS Command IO によって切り替えることができます。

    │ Prompt: GNSS Command IO
    │   Location:
    │     -> Examples
    │       -> GNSS CXD5603 @command emulator example (EXAMPLES_GNSS_ATCMD [=y])
    • Example uses USB CDC tty : 拡張ボード上の USB ポートを使用 (デフォルト)

    • Example uses STDINOUT for nsh debug UART : メイン基板上の USB ポート (UART1)

    • Example uses UART ttyS0 : メイン基板上の USB ポート (UART1)

    • Example uses UART ttyS1 : 非サポート

    • Example uses UART ttyS2 : メインボード及び拡張ボード上の UART ポート (UART2)

      ビルドに成功すると sdk フォルダ直下に nuttx.spk というバイナリファイルが生成されます。

  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

3.3.2. @コマンド仕様

ホストから送信するコマンドの仕様について説明します。

PC 等のホストから送信されたコマンドを gnss_atcmd アプリケーションが受信し、処理結果をホストに返します。コマンド送信から結果が応答されるまでの期間は、NMEA は出力されません。gnss_atcmd アプリケーションからコマンド完了を示す応答メッセージ (Done または Err ) が返ってくる前に別のコマンドを発行しないようにして下さい。なお、コマンド送信からコマンド応答が返るまでの時間はコマンドの種別およびその時の状態により異なりますが、ワーストケースで約 5 秒かかることがあります。ホストコントローラにてタイムアウトを検出する際は 5 秒にてタイムアウトと判断して下さい。

3.3.2.1. コマンドフォーマット

コマンドの書式は以下の通りです。"@" (アットマーク)に続いてコマンド文字列や引数を送信し、最後に改行コードを送ります。

@Command [Argument0] [Argument1]...<CR><LF>
Command

4 文字以内の文字列です。詳細は下表 コマンド一覧 を参照してください。

Argument

10 進数で表される数値です。文字列の先頭が "0x" の場合は 16 進数を表します。 コマンドによっては複数の引数をとります。

<CR><LF>

改行コードを表します。コマンド行の終わりには CR (Carriage Return) + LF (Line Feed) を付けてください。

3.3.2.2. 正常応答フォーマット

正常応答結果の書式は以下の通りです。[送信したコマンド文字列]に続いて、"Done"の文字列が返ります。

[Command] Done<CR><LF>
Command

受信したコマンド文字列を表します。

<CR><LF>

改行を表します。応答行の終わりには CR (Carriage Return) + LF (Line Feed) が付加されています。

3.3.2.3. エラー応答フォーマット

エラー応答結果の書式は以下の通りです。[送信したコマンド文字列]に続いて、"Err"の文字列とエラーコードが返ります。

[Command] Err ErrorCode<CR><LF>
Command

受信したコマンド文字列を表します。

ErrorCode

負値のエラーコードを表します。

<CR><LF>

改行を表します。応答行の終わりには CR (Carriage Return) + LF (Line Feed) が付加されています。

3.3.2.4. コマンドシーケンス

@GCD のような測位開始のコマンドを発行すると、"Done" の応答を返した後に、定期的に NMEA センテンスが Host へ送られます。

Diagram

@VER コマンドを発行すると、バージョン情報が送られた後に、"Done" の応答が返ります。

Diagram
3.3.2.5. コマンド一覧

gnss_atcmd が処理可能なコマンド一覧を下記に示します。

Command Argument Description

@AEXT

-

アプリケーションを終了します。本コマンドは測位停止中に実行してください。

@BSSL

NMEAマスク

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

NMEA Bit Description

$xxGGA

0

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

$xxGLL

1

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

$xxGSA

2

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

$xxGSV

3

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

$xxGNS

4

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

$xxRMC

5

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

$xxVTG

6

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

$xxZDA

7

年月日を含む時刻情報

$QZQSM

14

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

xx は、以下を表します。

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

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

  • GZ:QZS衛星で測位している場合

  • BD:BeiDou衛星で測位している場合

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

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

コマンド例:

$xxGGAのみ出力を設定

@BSSL 0x1<CR><LF>

$xxRMCと$QZQSMの出力を設定

@BSSL 0x4020<CR><LF>

@BUP

-

受信済みのエフェメリス及び各種パラメータを Flash へセーブします。セーブされたデータは次回起動時に自動的にリストアされます。 本コマンドは測位停止中に実行して下さい。

@GCD

-

Cold Start による測位を開始し、NMEAセンテンスを周期的に出力します。出力されるNMEAセンテンスはNMEAマスクに従います。

@GNS

衛星マスク

測位に使用する衛星を引数のビットマスク値で選択します。
本コマンドは測位停止中に実行してください。
例)GPSの場合は 0x1、Hybridの場合は0x3を指定

Bit Satellite

0

GPS

1

GLONASS

2

SBAS(WAAS)補強

3

QZSS L1C/A補完(みちびき)

4

予約

5

QZSS L1S補強(みちびき)

6

BeiDou

7

Galileo

NOTE
GLONASS, Galileo, BeiDou に関して同時に使用することはできません。

コマンド例:

GPSを選択

@GNS 0x1<CR><LF>

GPS+GLONASS+QZSS L1C/Aを選択

@GNS 0xb<CR><LF>

GPS+BeiDou+QZSS L1C/Aを選択

@GNS 0x49<CR><LF>

GPS+Galileo+QZSS L1C/Aを選択

@GNS 0x89<CR><LF>

@GPOE

<緯度[度]>
<緯度[分]>
<緯度[秒]>
<経度[度]>
<経度[分]>
<経度[秒]>

楕円体座標の受信機現在位置を設定します。

コマンド例:

北緯35°37’09”,東経139°43’51”

@GPOE 35 37 09 139 43 51<CR><LF>

北緯33°07’19”,西経117°19’18”

@GPOE 33 07 19 -117 19 18<CR><LF>

@GSR

-

Hot Start による測位を開始し、NMEAセンテンスを周期的に出力します。
出力されるNMEAセンテンスはNMEAマスクに従います。

@GSTP

-

測位を停止します。

@GSW

-

Warm Start による測位を開始し、NMEAセンテンスを周期的に出力します。
出力されるNMEAセンテンスはNMEAマスクに従います。

@GTIM

<年>
<月>
<日>
<時>
<分>
<秒>

UTC時刻を設定します。

コマンド例:

2018/2/1 13:30'30"

@GTIM 2018 02 01 13 30 30<CR><LF>

2018/7/10 00:00'00”

@GTIM 2018 07 10 00 00 00<CR><LF>

@VER

-

ALLゼロのバージョン番号を返します。

3.3.3. 動作確認

シリアルターミナルを開いて、gnss_atcmd コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から gnss_atcmd コマンドを実行します。

    以下に示す例は、コマンド入出力のターミナルとして NuttShell と同じメイン基板上の USB ポートを使用しています。 前述した GNSS Command IO のコンフィグレーションは、"Example uses STDINOUT for nsh debug UART" を選択しています。

    • Example uses USB CDC tty

    • Example uses STDINOUT for nsh debug UART

    • Example uses UART ttyS0

    • Example uses UART ttyS1

    • Example uses UART ttyS2

3.3.3.1. 例: Cold Start による測位の開始と停止

GPS、Glonass、QZSS L1C/A を測位衛星として選び、Cold Start で測位を開始します。 その後 NMEA センテンスが出力され、適当な期間の後、測位を停止して gnss_atcmd を終了します。

nsh> gnss_atcmd       (アプリケーション開始)
@GNS 0x0b↵         (測位衛星の選択)
@GCD↵             (Cold Start測位開始)

----- <NMEA出力> ----

@GSTP↵            (測位停止)
@AEXT↵           (アプリケーション終了)
nsh>

3.3.3.2. 例: GNSS 終了後の Hot Start 測位

初めに GPS、Glonass、QZSS L1C/A を測位衛星として選び、Cold Start で測位を開始します。 測位された後に、Spresense の電源は保ちつつ GNSS を終了し、再度 Hot Start で測位を開始します。 Hot Start では測位開始から数秒後に測位が FIX します。

nsh> gnss_atcmd       (アプリケーション開始)
@GNS 0x0b↵         (測位衛星の選択)
@GCD↵             (Cold Start測位開始)

----- <NMEA出力> ----

@GSTP↵            (測位停止)
@AEXT↵           (アプリケーション終了)

nsh> gnss_atcmd       (アプリケーション開始)
@GSR↵             (Hot Start測位開始)

----- <NMEA出力> ----

@GSTP↵           (測位停止)
@AEXT↵           (アプリケーション終了)
nsh>

3.3.3.3. 例: Spresense 電源 OFF 後の Hot Start 測位

初めに GPS、Glonass、QZSS L1C/A を測位衛星として選び、Cold Start で測位を開始します。 その後、GNSS を終了した後に Spresense の電源を OFF します。

再度 Spresense の電源を入れた後、UTC 時刻と現在位置を入力して Hot Start で測位を開始します。 Hot Start では測位開始から数秒後に測位が FIX します。

nsh> gnss_atcmd       (アプリケーション開始)
@GNS 0x0b↵         (測位衛星の選択)
@GCD↵             (Cold Start測位開始)

----- <NMEA出力> ----

@GSTP↵            (測位停止)
@BUP↵              (Flashへバックアップ)
@AEXT↵           (アプリケーション終了)

----- <Power OFF> -----

----- <Power ON> -----

nsh> gnss_atcmd       (アプリケーション開始)
@GTIM 2018 11 09 02 45 05↵   (UTC 時刻設定)
@GPOE 35 39 31 139 44 05↵  (現在位置の設定)
@GSR↵             (Hot Start測位開始)

----- <NMEA出力> ----

@GSTP↵           (測位停止)
@AEXT↵           (アプリケーション終了)
nsh>

3.3.3.4. みちびきQZQSMセンテンスの出力

衛星マスクにQZSS L1/CAを選び、NMEAマスクでQZQSMセンテンスを有効にした後、測位を開始します。 受信条件が良ければ10秒弱で最初のQZQSMセンテンスが出力され、その後4秒毎に出力されます。

nsh> gnss_atcmd

@GNS 0x29↵

@BSSL 0x40ef↵

@GCD↵

$GPGGA,000001.00,,,,,0,00,,,,,,,*49 $GNGLL,,,,,000001.00,V,N*55
…​
$GNZDA,000008.00,06,01,1980,,*77 $QZQSM,56,9AADF260540002C3F2587F8B101962082C41A588ACB1181623500011439023C*7B $GPGGA,000009.00,,,,,0,00,,,,,,,*41
…​

3.4. GNSS Add-on サンプルアプリケーション

GNSS Add-on サンプルアプリケーションの動作について説明します。

本サンプルアプリケーションは、GNSS Add-onボードを使って位置情報を取得するといった基本的な使い方に加えて、 GPSデータロガーとしてそのまま利用できるようないくつかの機能をオプションとして実装しています。 オリジナルアプリケーションを作成する場合の参考にしてください。

  • NMEA出力サポート

  • 測位周期設定サポート

  • 1PPS信号を使った時刻設定

  • ファイルロギング機能

  • 省電力のための間欠測位

  • アプリケーションの自動実行

  • みちびき災危通報出力サポート

3.4.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/gnss_addon を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/gnss_addon
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

3.4.2. 動作確認

シリアルターミナルを開いて、gnss_addon コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から gnss_addon コマンドを実行します。

    gnss_addon コマンドの引数に -h を指定すると Usage が表示されます。

    nsh> gnss_addon -h
    Usage: gnss_addon [-n] [-q] [-p] [-c <cycle>] [-f <fixcnt>] [-s <sleep>] [-o <filepath>]
    Options:
      -n: Enable NMEA output
      -q: Enable NMEA DC Report output with "-n" option
      -p: Enable 1PPS signal
      -c <cycle>: Positioning cycle
         (100, 125, 200, 250, 500 or 1000 x N) [msec] (default:1000)
      -f <fixcnt>: Positioning fix count (default:300)
      -s <sleep>: Sleeping time [sec] (default:0)
      -o <filepath>: Full path to a log file

    gnss_addon コマンドを実行すると、はじめにファームウェアのバージョンを表示した後、指定した引数にしたがって測位を開始します。 測位がFIXして位置情報を取得できてから -f で指定した数だけ連続して測位に成功すると測位動作を停止し、 -s で指定した秒数だけシステム全体がスリープ状態に入ります。アプリケーションの自動実行機能を利用することで、スリープ状態から起床したら再び測位を開始する、という間欠的な動作を繰り返し行うことができます。

  3. アプリケーションの自動実行を設定する

    アプリケーションの自動起動方法 に書かれている方法で、電源投入時に本アプリケーションが自動で実行されるように設定します。

    例えば、100回測位した後に60秒スリープするという周期動作を行う場合は、次のように init.rc スクリプトを作成します。

    nsh> echo "gnss_addon -f 100 -s 60" > /mnt/spif/init.rc

    reboot コマンド、もしくは、リセットボタンを押して再起動すると、スクリプトの内容に従って自動実行を開始します。

    nsh> reboot
    Run /mnt/spif/init.rc.
    sh [13:100]
    
    NuttShell (NSH) NuttX-12.3.0
    nsh> GNSS Add-on example application:
    NMEA: Disable (DC Report: Disable), 1PPS: Disable
    After positioning fix 100 times, sleep 60 sec.
    FW version: v00.144
    2000/01/02 00:00:03.000 0.000000 0.000000 0.000 [N]

    測位実行中でもNuttShellコマンドを入力することができます。自動起動を中止する場合は、 init.rc スクリプトを削除してから再起動してください。

    nsh> rm /mnt/spif/init.rc
    nsh> reboot

3.4.3. プログラム解説

  • 基本的な測位シーケンス

    ここでは、GNSS Add-onボードを使用して位置情報を取得するための基本的な手順を解説します。 内蔵GNSSとインターフェース互換性を保っているので、基本的な使い方は、GNSS サンプルアプリケーション と同様です。

    内蔵GNSSのデバイスファイル名は /dev/gps、GNSS Add-onドライバのデバイスファイル名は /dev/gps2 になります。

    GNSS デバイスファイル名

    内蔵GNSS

    /dev/gps

    GNSS Add-on

    /dev/gps2

    まず初めに /dev/gps2 のデバイスファイルを open() します。

      fd = open("/dev/gps2", O_RDONLY);

    openしたドライバに対して ioctl()CXD56_GNSS_IOCTL_START コマンドにより測位を開始します。

      ret = ioctl(fd, CXD56_GNSS_IOCTL_START, CXD56_GNSS_STMOD_HOT);
      if (ret < 0)
        {
          printf("ERROR: start ret=%d, errno=%d\n", ret, errno);
          goto errout;
        }

    周期的にあがってくる測位データを読み出す方法を以下に示します。

    測位情報の通知を poll() によって待ち合わせてから、read() を用いて測位データを読み出す、という動作を繰り返し行います。この通知の間隔は、後述する測位周期の設定に依存します。デフォルトでは、1秒に1回の間隔で通知がきます。 また、測位動作中であることを外部から監視できるように、toggle_led() 関数でメインボード上の LED3(GPIO_LED4) を点滅させています。

      /* Wait for positioning data to be notified. */
    
      ret = poll(fds, 1, -1);
      if (ret < 0)
        {
          printf("ERROR: poll ret=%d, errno=%d\n", ret, errno);
          break;
        }
    
      /* Read the positioning data. */
    
      ret = read(fd, &posdat, sizeof(posdat));
      if (ret != sizeof(posdat))
        {
          printf("ERROR: read ret=%d, errno=%d\n", ret, errno);
          break;
        }
    
      toggle_led();

    取得した測位データをシリアルターミナルへ出力するコードを以下に示します。

      /* Print the positioning data. */
    
      if (args.nmea)
        {
          print_nmea(&posdat);
        }
      else
        {
          print_posdat(&posdat, fp);
        }

    NMEAフォーマットでの出力方法については後述します。 デフォルトでは、UTC時刻、緯度[度]、経度[度]、高度[m] 及び測位ステータス情報を表示しています。 測位ステータス表示の意味は次の通りです。

    測位ステータス 状態

    [N]

    非測位状態

    [A]

    測位状態

    [D]

    測位状態かつDGPSによる補正が有効

    指定した回数だけ測位が完了したら、ioctl() を用いて測位を停止 (CXD56_GNSS_IOCTL_STOP) します。次回起動のためにバックアップデータを保存 (CXD56_GNSS_IOCTL_SAVE_BACKUP_DATA) した後、GNSS Add-onボード上のデバイスをディープスリープモード (CXD56_GNSS_IOCTL_SLEEP) に入れます。ディープスリープ状態にすることで、電源投入後のアイドル状態よりも消費電力を低減することができます。

      /* Stop GNSS, save the backup data and put it deep sleep mode
       * for power saving.
       */
    
      ret = ioctl(fd, CXD56_GNSS_IOCTL_STOP, 0);
      if (ret < 0)
        {
          printf("ERROR: stop ret=%d, errno=%d\n", ret, errno);
        }
    
      ret = ioctl(fd, CXD56_GNSS_IOCTL_SAVE_BACKUP_DATA, 0);
      if (ret < 0)
        {
          printf("ERROR: save ret=%d, errno=%d\n", ret, errno);
        }
    
      ret = ioctl(fd, CXD56_GNSS_IOCTL_SLEEP, CXD56_GNSS_DEEPSLEEP);
      if (ret < 0)
        {
          printf("ERROR: sleep ret=%d, errno=%d\n", ret, errno);
        }

    もし、ディープスリープモードからウェイクアップさせるときは、ioctl()CXD56_GNSS_IOCTL_WAKEUP コマンドを発行してください。本サンプルアプリケーションで open() 直後にウェイクアップコマンドを発行していますが、これは gnss_addon コマンドを繰り返し実行するときに、既にディープスリープモードにいるデバイスを起こすために行っています。このコマンドはデバイスが起床状態のときに発行しても特に問題ありません。

      /* Wakeup as GNSS may be in sleep mode. */
    
      ret = ioctl(fd, CXD56_GNSS_IOCTL_WAKEUP, 0);
      if (ret < 0)
        {
          printf("ERROR: wakeup ret=%d, errno=%d\n", ret, errno);
        }

    最後に、デバイスを close() します。

      /* Close a GNSS Add-on device driver. */
    
      ret = close(fd);
      if (ret < 0)
        {
          printf("ERROR: close ret=%d, errno=%d\n", ret, errno);
        }

    ここまでが測位を行うための基本シーケンスになります。これより先では、その他のオプション機能について説明します。

  • NMEA出力

    NMEA形式で出力を行う場合は、gnss_addon コマンドの引数に -n を追加してください。他の引数と組み合わせて使用することができます。

    nsh> gnss_addon -n [その他のオプション]

    NMEAを出力するためのコードは gnss_addon_nmea.c に実装されています。 出力するNMEAセンテンスを限定したいときは、次のコードから不要なものを削除してください。

      /* Select NMEA sentence */
    
      NMEA_SetMask2(NMEA_GGA_ON |
                    NMEA_GLL_ON |
                    NMEA_GSA_ON |
                    NMEA_GSV_ON |
                    NMEA_GNS_ON |
                    NMEA_RMC_ON |
                    NMEA_VTG_ON |
                    NMEA_QZQSM_ON |
                    NMEA_ZDA_ON);
  • みちびき災危通報出力

    みちびき災危通報の出力を行う場合は、gnss_addon コマンドの引数に -n-q を追加してください。他の引数と組み合わせて使用することができます。

    nsh> gnss_addon -n -q [その他のオプション]

    みちびき災危通報を受信するとNMEA出力の中に $QZQSM センテンスが出力されます。

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

  • 1PPS信号出力

    1PPS信号を出力する場合は、gnss_addon コマンドの引数に -p を指定してください。他の引数と組み合わせて使用することができます。

    nsh> gnss_addon -p [その他のオプション]

    1PPS信号の出力を有効にするには、ioctl()CXD56_GNSS_IOCTL_SET_1PPS_OUTPUT コマンドを発行します。

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

     ret = ioctl(fd, CXD56_GNSS_IOCTL_SET_1PPS_OUTPUT, 1);
     if (ret < 0)
       {
         printf("ERROR: 1pps ret=%d, errno=%d\n", ret, errno);
       }

    1PPS信号を使った時刻同期方法も本サンプルコードに実装されています。本サンプルでは1PPS信号をGPIO割り込みとして受信しているので、そのまま利用するためには、1PPS信号と PIN_I2S0_DATA_OUT ピンを接続する必要があります。CL2 ランドと PIN_I2S0_DATA_OUT ピンを外部で結線するか、もしくは、抵抗 R16 をショートしてください。

    測位データの中に含まれている時刻情報は、デバイスが測位したタイミングとSpresenseが通知を受け取って読み出しを行うタイミングとのズレがあり、実時間から数100ミリ秒遅れた時刻になっています。より正確な時刻を知りたい場合は、1PPS信号を利用することができます。測位データに含まれる時刻情報から秒未満を切り捨てて +1秒した時刻ちょうどに、次の1PPS信号が通知されます。例えば、測位データ内の時刻が12時34分56秒だった場合、これは実時刻よりも遅れた時刻になっていますが、次にPPS信号から割り込みを受けたタイミングが実時刻の12時34分57秒ちょうどになります。

    gnss_addon_pps.c サンプルコードでは、測位データから+1秒した時刻を覚えておいて、1PPS信号を割り込みとして受けたタイミングでその時刻をRTCにセットしています。割り込みハンドラからスレッドへのディスパッチ時間、RTCへの書き込み時間、及び、RTCの時刻精度が32kHzなので、ある程度の誤差は含まれますが、測位データ内の時刻よりは正確な実時間をRTCへ設定することができます。また、デバッグ用途で1PPS信号を割り込みとして受信したタイミングでメインボードの LED2(GPIO_LED3) を点滅させています。1PPS信号を割り込みとして正しく受け取れているかを確認することができます。

  • 測位周期

    gnss_addon コマンドの引数に -c <周期msec> を指定して測位周期を変更することができます。何も指定しなかった場合のデフォルト測位周期は1000msec(=1Hz)です。サポートしている周期は、100、125、200、250、500 もしくは 1000 の N 倍 (N≠0)です。サポート外の周期を設定した場合は、Invalid Parameter のエラーになりコマンドの実行を終了します。

    例えば、500msec(=2Hz)に設定する場合は次のように実行します。

    nsh> gnss_addon -c 500 [その他のオプション]

    測位周期の設定は、ioctl()CXD56_GNSS_IOCTL_SET_OPE_MODE コマンドを使用します。

      struct cxd56_gnss_ope_mode_param_s opemode;
    
      opemode.mode = 1;
      opemode.cycle = args.cycle;
    
      ret = ioctl(fd, CXD56_GNSS_IOCTL_SET_OPE_MODE, (uint32_t)&opemode);
      if (ret < 0)
        {
          printf("ERROR: cycle ret=%d, errno=%d\n", ret, errno);
          goto errout;
        }
  • ファイルロギング機能

    gnss_addon コマンドの引数に -o <ファイル名> を指定することで、測位結果をシリアルに出力する代わりにファイルへ保存します。拡張ボードにSD カードを挿して、SDカード上の nmea.log にログファイルを記録する場合は次のように実行します。

    nsh> gnss_addon -n -s 60 -o /mnt/sd0/nmea.log [その他のオプション]

    SDカードへのファイルの保存は、スリープする直前にファイルを fclose() したタイミングで行われます。そのため、-s オプションでスリープ時間を設定しておき、スリープしている間(測位中のLEDが点滅していないとき)にSDカードを引き抜くか電源を落とすことで、保存したファイルを安全に取り出すことができます。

    もし、NMEA出力をスリープ直前ではなく即時ファイルへの書き込みを行いたい場合は、CONFIG_EXAMPLES_GNSS_ADDON_FSYNC_LOGGING を有効にしてください。gnss_addon_nmea.coutnmea() 関数内で fsync() を発行することで毎回ファイルへの書き出しが行われます。ただし、このオプションを有効にした場合、SDカードへの書き込みに時間がかかること、及び、ファイル書き込み中に電源を落としてしまうリスクが高くなるので使い方には十分に注意してください。

    static int outnmea(char *buf)
    {
      int ret = fprintf(g_stream, "%s", buf);
    #ifdef CONFIG_EXAMPLES_GNSS_ADDON_FSYNC_LOGGING
      fsync(fileno(g_stream));
    #endif
      return ret;
    }
  • RTCアラーム設定

    -s オプションによりスリープ時間が設定されているとき、alarm コマンドを用いてRTCアラームを設定した後にシステムをスリープさせます。アプリケーションプログラム内から system() 関数を利用することで alarm コマンドを実行しています。これを実現するために、configs/examples/gnss_addon/defconfig コンフィグレーションファイルでは、CONFIG_EXAMPLES_ALARM=yCONFIG_SYSTEM_SYSTEM=y を有効にしています。RTCアラーム設定をプログラム上で実装することもできますが、手軽に外部コマンドを実行する方法として参考にしてください。

      printf("RTC alarm after %d sec\n", args.sleep);
      snprintf(command, sizeof(command), "alarm %d", args.sleep);
      system(command);
    
      boardctl(BOARDIOC_POWEROFF, 0);

    system() 関数で alarm コマンドを発行した後に、boardctl() 関数を用いて、システム全体をディープスリープ状態へ遷移させています。ディープスリープ状態は、電源OFFに近しいレベルまで消費電力を削減します。

    ディープスリープ中にアラームタイマーが発火すると、システムはディープスリープ状態から起床して電源ON時と同じブートシーケンスで再び起動します。

    プログラム内から up_pm_get_bootcause() 関数を使って起動要因を取得することで、電源投入による起動なのか、ディープスリープからの起床なのかを判別することができます。

      /* Get the boot cause and executes different processes.
       * Specifically, if the system is started by RTC from DeepSleep state,
       * it injects the RTC time to GNSS and run GNSS hot start.
       */
    
      bootcause = up_pm_get_bootcause();

    本アプリケーションでは、この起動要因をみてディープスリープ状態からの起床だった場合は、以下のコードを実行します。

      /* If the system is started by RTC from DeepSleep state,
       * set the RTC time to GNSS since the GNSS does not keep time.
       */
    
      if (bootcause == PM_BOOT_DEEP_RTC)
        {
          struct cxd56_gnss_datetime_s datetime;
    
          get_datetime(&datetime);
    
          ret = ioctl(fd, CXD56_GNSS_IOCTL_SET_TIME, &datetime);
          if (ret < 0)
            {
              printf("ERROR: settime ret=%d, errno=%d\n", ret, errno);
            }
        }

    ディープスリープ中でもRTC時刻は保持されています。スリープ前にRTCへ時刻を設定しているので、起床後に get_datetime() 関数の中でRTCから現在時刻を取得します。現在時刻を ioctl()CXD56_GNSS_IOCTL_SET_TIME コマンドを用いてデバイスに対して時刻注入を行います。前回測位したときの位置やエフェメリス情報などはデバイス上のバックアップに保存されているので、ホットスタート測位が可能となり、測位がFIXするまでの時間(TTFF)を大幅に短縮できます。ただし、エフェメリスの有効期限切れ(通常3-4時間)や前回測位した位置から大きく離れた場所で測位した場合など、コールドスタート測位になりTTFFが長くなる可能性はありますのでご注意ください。

3.4.4. 補足

GNSS Add-onボード用ドライバのインターフェースは内蔵GNSSと互換性をもっています。

本章で説明した gnss_addon サンプルアプリケーションの他に、既存のGNSSアプリケーションもGNSS Add-onボード上で簡単に動作させることができます。具体的には、コンフィグレーションを実行する際に、引数に feature/gnss_addon を追加すれば GNSS Add-on ボード上で動作するようになります。各サンプルの詳しい説明は、それぞれのチュートリアルを参照してください。

サンプルアプリケーション コンフィグレーション方法

gnss

$ tools/config.py feature/gnss_addon examples/gnss

gnss_atcmd

$ tools/config.py feature/gnss_addon examples/gnss_atcmd

lte_lwm2m

$ tools/config.py feature/gnss_addon examples/lte_lwm2m

ambient_gnsslogger

$ tools/config.py feature/gnss_addon examples/ambient_gnsslogger

awsiot_gnsslogger

$ tools/config.py feature/gnss_addon examples/wifi_awsiot_gnsslogger

4. AudioLite チュートリアル

4.1. audiolite サンプルアプリケーション

この章では、Spresenseのaudioliteを用いたサンプルについて解説します。
このサンプルは、SDKv3.0.0から追加されたaudioliteの基本的な使い方をご理解いただくために用意しています。

4.1.1. audioliteについて

audioliteは、よりシンプルにSpreenseのAudio機能と利用するためのC++のライブラリです。 SDKv3.0.0では、既存のAudioが提供する一部機能が未対応になっており、次回以降のバージョンで対応予定です。 なお、排他利用にはなりますが、既存のAudioライブラリも今まで通り利用可能です。

4.1.2. サンプルコード

audioliteを用いたサンプルは、以下の3つになります。

  • audiolite_mp3player

  • audiolite_wavplayer

  • audiolite_wavrecorder

4.1.2.1. audiolite_mp3player

audioliteライブラリが提供する、audiolite_mp3decコンポーネントとaudiolite_outputcompコンポーネントを接続したシンプルなMP3プレーヤーのサンプルになります。

ソースコード概要

このサンプルでは以下の構造を作成して、引数で指定されたファイル名のMP3ファイルを再生し、曲が終了するとサンプルが終了します。

Diagram
図 2. Structure of the sample app

このコードではまず、audioliteから各種イベントを受け取るために、audiolite_eventlistenerを継承したmy_mp3listenerクラスを定義しています。システムが発行するイベントを受け取るには、audiolite_eventlistenerのon_event()メソッドをオーバーライドする必要があるため、自前のon_event()メソッドを追記しています。また、メンバ変数として、bool型のplayingを定義して、演奏終了イベントAL_EVENT_DECODEDONEを受信した際に、falseにすることで、演奏終了をアプリのメインスレッドに知らせるようにしています。

メインスレッドの中では、必要なインスタンスを生成して、上記構成に沿って各インスタンスの初期化を行います。

まず、コマンド引数で指定されたファイルのオープンを行います。

  if (fstream->rfile(argv[1]) != OK)

audiolite_filestreammクラスのrfile()メソッドは読み出しモードでファイルを開きます。

audiolite_set_systemparam(48000, 16, 2)は、システムパラメータとして、サンプリング周波数、1サンプル当たりのビット長、チャネル数を設定しています。 このパラメータでオーディオジャックのDACに音声が出力されます。

audiolite_set_evtlistener(&lsn)は、冒頭で説明したmy_mp3listenerのインスタンスをシステムに登録し、イベントの受信をできるようにしています。

imempool→create_instance(4096, 8)では、ファイルから読み出したMP3のストリームを保持するメモリプールの初期化をしています。この例では、4096バイトのブロックを8枚保持するメモリープールを作成しています。

omempool→create_instance(4096, 16)は、MP3ファイルのデコード結果(PCMデータ)を格納するためのメモリープールを作成しています。4096バイトを16枚。

作成したメモリープールをMP3デコーダーインスタンスにセットしているのが下記のコードになります。

  mp3->set_mempool(imempool);
  mp3->set_outputmempool(omempool);

デコーダーは、デコードするためのストリームを取得するaudiolite_streamを継承したクラスインスタンスを要求します。このサンプルではMP3をファイルから読み込んで実行するため、audiolite_filestreamクラスを用いています。 実際にaudiolite_streamをMP3デコーダーに設定しているのが以下のコードになります。

  mp3->set_stream(fstream);

デコードしたデータをスピーカーに出力するために、出力を担う、audiolite_outputcompクラスのインスタンスとaudiolite_mp3decのインスタンスを接続するコードが、以下のコードになります。

  mp3->bind(aoutdev);

以上で初期化が終了して、冒頭に記載した構成を構築することが出来たことになります。 あとは、my_mp3listenerのplayingメンバをtrueにして、終了時trueからfalseになった際にそれを検出できるように初期化を行ったのち、 audiolite_mp3decのstart()メソッドを呼ぶことで、指定されたファイルの再生を開始します。

  ret = mp3->start();
ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/audiolite_mp3player を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/audiolite_mp3player
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
動作確認

再生したいMP3ファイルをSDカードに保存して、SpresenseのExtensionボードに挿入します。

シリアルターミナルを開いて、audiolite_mp3player コマンドを実行します。

nsh>プロンプトからaudiolite_mp3playerと入力し、その後ろにファイルパスを指定してEnterを押し、実行します。

nsh> audiolite_mp3player <mp3 ファイルパス>

仮に再生したいMP3ファイルが、SDカードにmusic.mp3として保存されている場合、

nsh> audiolite_mp3player /mnt/sd0/music.mp3

と実行します。 ファイル名が正しければ音楽がイヤホンジャックから流れます。

4.1.2.2. audiolite_wavplayer

audioliteライブラリが提供する、audiolite_wavdecコンポーネントとaudiolite_outputcompコンポーネントを接続したシンプルなWAVファイルプレーヤーのサンプルになります。

ソースコード概要

このサンプルでは以下の構造を作成して、引数で指定されたファイル名のWAVファイルを再生し、曲が終了するとサンプルが終了します。

Diagram
図 3. Structure of the sample app

ソースコードの構造は、audiolite_mp3playerとほぼ同一で、異なる点は、audiolite_mp3decをaudiolite_wavdecに置き換えた構造になります。 また、WAVファイルの場合、デコード処理が必要ないため、MemoryPoolはPCMデータを格納するために1つのみ利用しています。

このコードではまず、audioliteから各種イベントを受け取るために、audiolite_eventlistenerを継承したmy_wavlistenerクラスを定義しています。システムが発行するイベントを受け取るには、audiolite_eventlistenerのon_event()メソッドをオーバーライドする必要があるため、自前のon_event()メソッドを追記しています。また、メンバ変数として、bool型のplayingを定義して、演奏終了イベントAL_EVENT_DECODEDONEもしくはAudioDriverのDMA停止イベントAL_EVENT_STOPOUTPUTを受信した際に、falseにすることで、演奏終了をアプリのメインスレッドに知らせるようにしています。

メインスレッドの中では、必要なインスタンスを生成して、上記構成に沿って各インスタンスの初期化を行います。

まず、コマンド引数で指定されたファイルのオープンを行います。

  if (fstream->rfile(argv[1]) != OK)

audiolite_filestreammクラスのrfile()メソッドは読み出しモードでファイルを開きます。

audiolite_set_systemparam(48000, 16, 2)は、システムパラメータとして、サンプリング周波数、1サンプル当たりのビット長、チャネル数を設定しています。 このパラメータでオーディオジャックのDACに音声が出力されます。

audiolite_set_evtlistener(&lsn)は、冒頭で説明したmy_wavlistenerのインスタンスをシステムに登録し、イベントの受信をできるようにしています。

mempool→create_instance(4096, 8)では、ファイルから読み出したPCMデータを保持するメモリプールの初期化をしています。この例では、4096バイトのブロックを8枚保持するメモリープールを作成しています。

作成したメモリープールをWAVデコーダーインスタンスにセットしているのが下記のコードになります。

  wavdec->set_mempool(mempool);

デコーダーは、デコードするためのストリームを取得するaudiolite_streamを継承したクラスインスタンスを要求します。このサンプルではWAVをファイルから読み込みを行うため、audiolite_filestreamクラスを用いています。 実際にaudiolite_streamをWAVデコーダーに設定しているのが以下のコードになります。

  wavdec->set_stream(fstream);

デコードしたデータをスピーカーに出力するために、出力を担う、audiolite_outputcompクラスのインスタンスとaudiolite_mp3decのインスタンスを接続するコードが、以下のコードになります。

  wavdec->bind(aoutdev);

以上で初期化が終了して、冒頭に記載した構成を構築することが出来たことになります。 あとは、my_wavlistenerのplayingメンバをtrueにして、終了時trueからfalseになった際にそれを検出できるように初期化を行ったのち、 audiolite_wavdecのstart()メソッドを呼ぶことで、指定されたファイルの再生を開始します。

  ret = wavdec->start();
ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/audiolite_wavplayer を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/audiolite_wavplayer
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
動作確認

再生したいWAVファイルをSDカードに保存して、SpresenseのExtensionボードに挿入します。

シリアルターミナルを開いて、audiolite_wavplayer コマンドを実行します。

nsh>プロンプトからaudiolite_wavplayerと入力し、その後ろにファイルパスを指定してEnterを押し、実行します。

nsh> audiolite_wavplayer <wav ファイルパス>

仮に再生したいWAVファイルが、SDカードにmusic.wavとして保存されている場合、

nsh> audiolite_wavplayer /mnt/sd0/music.wav

と実行します。 ファイル名が正しければ音楽がイヤホンジャックから流れます。

4.1.2.3. audiolite_wavrecorder

audioliteライブラリが提供する、audiolite_wavencコンポーネントとaudiolite_inputcompコンポーネントを接続したシンプルなWAVファイルレコーダーのサンプルになります。 また、ユーザーが独自のコンポーネントを実装して信号処理を行うことを想定した、audiolite_componentを継承したmy_interceptorクラスを定義し、audiolite_inputcompとaudiolite_wavencの間に挟むことで、audiolite_inputcompが送ってきているPCMデータをコンソールに出力しながらWAVファイルのレコーディングを行います。

ソースコード概要

このサンプルでは以下の構造を作成して、引数で指定されたファイル名のプレフィックスをベースに10秒間の録音を行います。

Diagram
図 4. Structure of the sample app

このコードではまず、イベントを取得用に、audiolite_eventlistenerを継承したmy_wavlistenerクラスを定義しています。受け取るイベントによって動作を変えることが無いため、on_event()では、単に受け取ったイベントの表示のみを行っています。

次に、流れるAudioPCMデータを受け取って表示するために、audiolite_componentを継承したmy_interceptorクラスを定義しています。 audiolite_componentのon_data()メソッドを継承することで、流れてきたデータが到達した際に、my_interceotpr::on_data()メソッドが呼ばれてデータを受け取ることが出来ます。

class my_interceptor : public audiolite_component
{
  public:
    void on_data()
    {
      ....
    }
}

on_data()の中では、到達したデータを受け取るためにaudiolite_inputnodeのインスタンス配列の0番目から、pop_data()メソッドを使ってaudiolite_memのインスタンスを取得しています。メモリプールとしてaudiolite_mempoolapbufを使っているため、audiolite_memを継承したaudiolite_memapbufにキャストして受け取ります。

      audiolite_memapbuf *mem = (audiolite_memapbuf *)_ins[0]->pop_data(NULL);

受け取ったaudiolite_memから実際のデータにアクセスするには、audiolite_memのget_data()メソッドでデータが格納されているメモリのポインタを取得します。

          int16_t *data = (int16_t *)mem->get_data();

後に設定するシステムパラメータで16bit / 2chのサンプルを指定しているため、データは、16bit毎に以下のように並びます。 Ch1, Ch2, Ch1, Ch2,…​.. このサンプルでは、データの末尾4サンプル分のCh1とCh2のデータを表示しています。

          printf("DL %d : %d\n", samples, data[(samples - 4) * 2]);
          printf("DL %d : %d\n", samples, data[(samples - 3) * 2]);
          printf("DL %d : %d\n", samples, data[(samples - 2) * 2]);
          printf("DL %d : %d\n", samples, data[(samples - 1) * 2]);
          printf("DR %d : %d\n", samples, data[(samples - 4) * 2 + 1]);
          printf("DR %d : %d\n", samples, data[(samples - 3) * 2 + 1]);
          printf("DR %d : %d\n", samples, data[(samples - 2) * 2 + 1]);
          printf("DR %d : %d\n", samples, data[(samples - 1) * 2 + 1]);

取得したメモリは後段のコンポーネントに送るため、audiolite_outputnodeクラスのインスタンス配列の_outs[0]に対してpush_data()メソッドを呼び出します。

          _outs[0]->push_data(mem);

最後に自身のメモリの利用が終わったため、そのメモリを解放します。

          mem->release();

メインタスクは、まず上述の構造を作成するためのクラスインスタンスを作成しています。

audiolite_set_systemparam(48000, 16, 2)は、システムパラメータとして、サンプリング周波数、1サンプル当たりのビット長、チャネル数を設定しています。 このパラメータでマイクから音がADCされます。

audiolite_set_evtlistener(&lsn)は、冒頭で説明したmy_wavlistenerのインスタンスをシステムに登録し、イベントの受信をできるようにしています。

mempool→create_instance(4096, 16)では、ファイルから読み出したPCMデータを保持するメモリプールの初期化をしています。この例では、4096バイトのブロックを16枚保持するメモリープールを作成しています。SDカードへの書き込みの遅延でOverflowが起きる場合、このサイズを調整することで改善が期待できます。

続いてWAVエンコーダーに出力先として、audiolite_filestreamを設定します。

  wavenc->set_stream(fstream);

ファイルのオープン等はwavencが直接行うため、ユーザー側のコードでは、filestreamを設定するのみになります。

wavencはWAVのサイズによってファイルを分割する機能を持っています。 そのため、ファイルのプレフィックスのみを設定します。ファイルのプレフィックスはコマンドラインオプションとして指定する仕様になるため、argv[1]をセットします。

  wavenc->set_fileprefix(argv[1]);

そして、データの生成元である、aduiolite_inputcompに対して、作成したメモリプールを設定します。

  aindev->set_mempool(mempool);

最後に、使用するコンポネントaudiolite_inputcomp、my_intercepter、audiolite_wavencを接続します。

  aindev->bind(intercept);
  intercept->bind(wavenc);

これで準備は完了です。 データの生成元であるaudiolite_inputcompコンポーネントに対してstart()メソッドを呼び、録音を開始します。

  ret = aindev->start();

あとは録音時間(このアプリでは10秒)経過するのを待ち、stop()メソッドで録音を停止します。

  aindev->stop();
ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/audiolite_wavrecorder を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/audiolite_wavrecorder
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
動作確認

シリアルターミナルを開いて、audiolite_wavrecorder コマンドを実行します。

nsh>プロンプトからaudiolite_wavrecorderと入力し、その後ろに保存するファイルのプレフィックスを入力してEnterを押して実行します。

nsh> audiolite_wavrecorder <wav ファイルのプレフィックス>

仮に保存先のファイル名を、SDカード上のrec_00.wavとして保存したい場合、

nsh> audiolite_wavrecorder /mnt/sd0/rec

と実行します。 ファイル名の後ろの_00.wavは、wavencが自動的に付加します。 正しく実行できれば、my_interceptorによりPCMデータを表示しながら、WAVファイルが保存されます。 保存されたWAVファイルは、audiolite_wavplayerを用いて再生することが出来ます。

5. Audio チュートリアル

5.1. Audio Player サンプルアプリケーション

Audio Player サンプルアプリケーションの動作について説明します。

5.1.1. ビルド&ロード手順

ここではコマンドラインによるビルド手順を示します。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/audio_player を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/audio_player
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
  4. Audio Playerの場合、デコード処理を行うDSPバイナリのロードが必要です。 DSPバイナリの置き場所は、SDカードか、SPI-Flashのどちらかを選択できますが、 ここでは、SDカードからロードする方法を行います。

    DSPバイナリのパスの指定は、アプリケーションコード(audio_player_main.cxx)内で指定します。 audio_player_main.cxx では DSPBIN_FILE_PATH で指定しています。

    #define DSPBIN_FILE_PATH "/mnt/sd0/BIN"

    であれば、SDカードが指定されています。

    SPI-flashにしたい場合は、"/mnt/spif/BIN" を指定してください。

    この "/mnt/sd0/BIN" は、SDカードをPCで読み込んだ時の、ルートディレクトリの下の BIN/ です。
    このディレクトリを作成し、ここに必要なコーデックのDSPを置きます。

    MP3を再生(デコード)する場合は、
    spresense/sdk/modules/audio/dsp/ の下の MP3DEC を選択してください。

  5. 同時に、再生したい音楽ファイルをSDカードに書き込みます。 audio_player_main.cxx では、 PLAYBACK_FILE_PATH で指定しています。

    #define PLAYBACK_FILE_PATH "/mnt/sd0/AUDIO"

    このため、SDカードをPC上で読み込んだ時に、ルートディレクトリの下の AUDIO/ 以下に、 再生したいオーディオファイルを置いて下さい。サブディレクトリに置くこともできます。

  6. 現在のAudio Playerサンプルでは、簡易PlayListを利用した再生を行っています。 このため、playlistファイルの置き場所とファイル名を指定して再生します。 audio_player_main.cxx では、 PLAYLIST_FILE_PATH でパスを指定、 PLAYLIST_FILE_NAME でファイル名を指定しています。

    #define PLAYLIST_FILE_PATH "/mnt/sd0/PLAYLIST"
    #define PLAYLIST_FILE_NAME "TRACK_DB.CSV"

    このため、SDカードをPC上で読み込んだ時の、ルートディレクトリの下の PLAYLIST/ 以下に、 TRACK_DB.CSV というファイルを作成してください。

    TRACK_DB.CSV の中身は、 spresense/sdk/modules/audio/playlist/ の下の README.txt を参照してください。

これらすべてを用意することで、音楽再生が行えます。

5.1.2. Audio Playerの動作確認

この nuttx.spk を実機へロードするとAudio Playerのプログラムが実行されます。

Helloサンプルと同様に、シリアルターミナルを開きます。

minicom -D /dev/ttyUSB0 -b 115200 -s

builtinされている、 audio_player アプリを実行すると、

tutorial player log
図 5. 音楽再生時ログ

というログが表示され、音声が再生されます。

エラーが発生した場合は、オーディオサブシステムのエラーについて を参照してください。

5.1.3. 付録: 独自の信号処理を行う

これまでのチュートリアルで、オーディオプレーヤーとして十分な機能を実現することができました。
ここからは、より高度な処理を行う方法を説明していきます。

AudioPlayerでは、再生する音声に対してユーザー独自の信号処理を行うことが出来ます。
これを行いたい場合にはコンフィグメニューで [Use Postprocess] を有効にする必要があります。

  1. Postprocessを有効にします

    tools/cofig.py -m
    

    [Use Postprocess] にチェックを入れます。

    [Examples]
      [Audio player example]
        [Use Postprocess]        <= Y
    Postrocessの詳細についてはSDKデベロッパーガイドの Set preprocess を参照して下さい。
  2. ビルドを行います。

    make
    

    全ての手順がうまくいけば、 POSTPROC バイナリが spresense/examples/audio_player/worker/ の下に作成されます。
    これをSDカードの /mnt/sd0/BIN (PCから見ると BIN/ )フォルダに置いてください。

    本サンプルアプリケーションでは、 POSTPROC には簡単なRCfilterがデフォルトで組み込まれています。
    独自の信号処理などにカスタムをしたい場合には こちら を参考にして下さい。

    [Use Postprocess] の有効、無効で音声ファイルを再生し違いを確認してみてください。
    再生方法はこちら を参考にして下さい。

5.1.4. DSPバイナリ(POSTPROC)のカスタムについて

DSPバイナリ(POSTPROC)のカスタム方法について記載します。

5.1.4.1. Step1. POSTPROCのコード編集

POSTPROCのコード構成と編集箇所について説明します。
コードは、ユーザー編集すべき部分とフレームワークとして提供される部分の2つに分かれます。

ユーザーが編集するコード

ユーザーが主に編集すべきコードです。
これらのコードを編集してDSPで信号処理を記述することが出来ます。

worker ディレクトリの中にDSPのコードがまとまっており、その中に userproc ディレクトリがあります。
ユーザが信号処理などを書くのは userproc ディレクトリ内のみで、その他は基本的に変更の必要はありません。

main.cpp には、起動処理~MainCPUとのデータ通信制御が書かれていますので変更しないで下さい。

Diagram
図 6. コード構成
main.cpp

起動処理やDSP通信処理などが書かれています。編集の必要は有りません。

userproc_command.h

DSPとの通信コマンドを定義するヘッダファイルです。
必要なパラメータはこのファイルに記載します。

userproc.h

ユーザーコードのヘッダファイルです。

userproc.cpp

ユーザーコード本体です。
このファイルに信号処理などを書く、または呼び出します。

ユーザーコードに用意されるインタフェース

userproc.cpp には、 Init , Exec , Flush , Set コマンドの枠組みが用意されており
ユーザーコードはそれぞれに対応する中身を書いていくことでDSP内での処理を実現することが出来ます。

ユーザーが記述すべき処理について説明します。
(※デフォルトではサンプルとしてRCフィルタが組み込まれています。)

コマンドによるDSP内部の状態遷移は下図の通り行われます。

Diagram
図 7. DSPの状態遷移

各コマンドを使い以下のような流れで処理を行います。

  1. AUDCMD_INIT_OUTPUTMIXERがコールされるとDSPが起動します。

  2. Init コマンドで必要なパラメータ(ch数やビット長など)を設定します。

  3. 再生開始するとキャプチャした音声データが Exec コマンドで定期的にDSPに送られるので所望のフィルタ処理をしてください。

  4. 任意のタイミングでDSP内部のパラメータなどを変更したい場合には、 Set コマンドを送ることで実装することを想定しています。このコマンドの実行タイミングは、 Exec を含めた受信順になります。+

  5. 再生停止すると最後の音声データの Exec の後、 Flush コマンドが送られるので終端処理の必要がある場合はここで処理をします。

コマンドの定義

各機能で使用するデータ型は userproc_command.h に書かれており、中身は自由に書き換えることが出来ます。

各コマンドのフォーマットは下図の様になっています。
先頭の白抜きの部分には最低限必要なパラメータが固定で配置されています。これらは変更しないで下さい。
ユーザーが userproc_command.h で定義するパラメータは下図の User param (ピンク色箇所)にあたる部分です。

Diagram
図 8. コマンドフォーマット

各コマンドについて説明します。

struct InitParam : public CustomprocCommand::CmdBase
  • Init処理用のパラメータです。
    デフォルトではすべてreserveとなっていますが、ch数やビット長など、必要なパラメータに変更してください。

struct ExecParam : public CustomprocCommand::CmdBase
  • Exec処理用のパラメータです。
    音声データのアドレス・サイズは上図の ExecParam にある様に継承元の CustomprocCommand::ExecParamBase に定義されています。
    詳細は /sdk/modules/include/audio/dsp_framework/customproc_command_base.h を参照してください。

struct FlushParam : public CustomprocCommand::CmdBase
  • Flush処理用のパラメータです。
    音声データのアドレス・サイズは上図の ExecParam にある様に継承元の CustomprocCommand::FlushParamBase に定義されています。
    詳細は /sdk/modules/include/audio/dsp_framework/customproc_command_base.h を参照してください。

struct SetParam : public CustomprocCommand::CmdBase
  • Set処理用のパラメータです。
    様々な動的に変更したいパラメータを定義します。デフォルトではRCフィルタのOn/Offや係数を定義しています。

各機能に対応する関数

下記の関数群は userproc.cpp に書かれています。中身は自由に書き換えることが出来ます。
それぞれのコマンド定義に従い処理を行います。

void UserProc::init(InitParam *)
  • InitParamに従い初期化を行う処理を書いて下さい。
    アプリケーションコードからの AUDCMD_INITMPP コマンドで実行されます。
    (デフォルトでは何もしていません。)

void UserProc::exec(ExecParam *)
  • ExecParamに従い信号処理を書いて下さい。
    再生を開始するとSDK内部から定期的に呼ばれます。
    1フレームはLPCMの場合には640サンプル、MP3の場合には1152(ただし16kHz時は1728)サンプルです。
    入力データアドレスからデータを取得し、信号処理を行い、出力データアドレスへ書き出して下さい。
    (デフォルトではRCフィルタ処理が書かれています。)

void UserProc::flush(FlushParam *)
  • FlushParamに従いFlush(終端)処理を書いて下さい。
    再生の停止時にSDK内部から一度だけ呼ばれます。
    IIRやFIRフィルタのように遅延が発生する場合は、最終フレームの後に flush を行い、遅延分の出力を行います。
    出力すべきデータがある場合には、出力データアドレスへ書き出して下さい。
    (デフォルトでは何もしていません。)

void UserProc::set(SetParam *)
  • SetParamに従い設定処理を書いてください。
    アプリケーションコードからの AUDCMD_SETMPPPARAM コマンドで実行されます。
    (デフォルトではRCフィルタの係数を設定しています。)

5.1.4.2. Step2. POSTPROC バイナリのビルド

ConfigurationUser Postprocess を有効にしていれば、本アプリケーションのビルド時に自動で POSTPROC バイナリが作成されます。
作成されるパスは spresense/examples/audio_player/worker/ の下の POSTPROC です。
これをSDカードの /mnt/sd0/BIN (PCから見ると \BIN )フォルダに置いてください。

5.2. Audio Recorder サンプルアプリケーション

Audio Recorderのサンプルアプリケーションの動作について説明します。

5.2.1. ビルド&ロード手順

ここではコマンドラインによるビルド手順を示します。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/audio_recorder を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/audio_recorder
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
  4. Audio Recorderの場合、エンコード処理を行うDSPバイナリのロードが必要です。 DSPバイナリの置き場所は、SDカードか、SPI-Flashのどちらかを選択できますが、 ここでは、SDカードからロードする方法を行います。

    DSPバイナリのパスの指定は、アプリケーションコード内で指定します。
    audio_recorder_main.cxx では、 DSPBIN_PATH で設定していますので必要に応じてアプリケーションコードを変更して下さい。

    #define DSPBIN_PATH "/mnt/sd0/BIN"

    であれば、SDカードが指定されています。

    この /mnt/sd0/BIN は、SDカードをPCで読み込んだ時の、ルートディレクトリの下の BIN/ です。
    このディレクトリを作成し、ここに必要なコーデックのDSPを置きます。

    SPI-flashにしたい場合は、/mnt/spif/BIN を指定してください。

    MP3で録音(エンコード)する場合は、
    spresense/sdk/modules/audio/dsp/ の下の MP3ENC を選択してください。

    その他のエンコードをする場合のCodec種別とDSPバイナリの組み合わせは下表の通りです。

    Codec DSPバイナリ

    MP3

    MP3ENC

    LPCM

    SRC

5.2.2. Audio Recorderの動作確認

この nuttx.spk をSpresenseへロードするとAudio Recorderのプログラムが実行されます。
Helloサンプルと同様に、シリアルターミナルを開きます。

minicom -D /dev/ttyUSB0 -b 115200 -s

builtinされている、 audio_recorder アプリを実行すると、

tutorial recorder log
図 9. 音声記録時ログ

というログが表示され、音声が記録されます。

記録された音声はPCで再生することが出来ます。その際にはSDカードルートディレクトリの下の REC/ に音声ファイルがあります。
audio_recorder_main.cxx では、 RECFILE_ROOTPATH で設定していますので必要に応じてアプリケーションコードを変更して下さい。

#define RECFILE_ROOTPATH "/mnt/sd0/REC"

であればSDカードのルートの下の REC/ に音声ファイルが作られます。

エラーが発生した場合は、オーディオサブシステムのエラーについて を参照してください。

5.2.3. 付録: 独自の信号処理を行う

これまでのチュートリアルで、レコーダーとして十分な機能を実現することができました。
ここからは、より高度な処理を行う方法を説明していきます。

AudioRecorderでは記録する音声に対してユーザー独自の信号処理を行うことが出来ます。
これを行いたい場合にはコンフィグメニューで [Use preprocess] を有効にする必要があります。

  1. Preprocessを有効にします

    コンフィグメニューを開きます。

    tools/cofig.py -m
    

    [Use preprocess] にチェックを入れます。

    [Examples]
      [Audio recorder example]
        [Use preprocess]        <= Y
    Preprocessの詳細についてはSDKデベロッパーガイドの Set preprocess を参照して下さい。
  2. ビルドを行います。

    make
    

    全ての手順がうまくいけば、 PREPROC バイナリが spresense/examples/audio_recorder/worker/ の下に作成されます。
    これをSDカードの /mnt/sd0/BIN (PCから見ると BIN/ )フォルダに置いてください。

    本サンプルアプリケーションでは、 PREPROC には簡単なRCfilterがデフォルトで組み込まれています。
    独自の信号処理などにカスタムをしたい場合には こちら を参考にして下さい。

    Preprocessの有効、無効で記録した音声ファイルを再生して、違いを確認してみてください。
    記録、再生方法はこちら を参考にして下さい。

    == DSPバイナリ(PREPROC)のカスタムについて

DSPバイナリ(PREPROC)のカスタム方法について記載します。

5.2.3.1. Step1. PREPROCのコード編集

PREPROCのコード構成と編集箇所について説明します。
コードは、ユーザー編集すべき部分とフレームワークとして提供される部分の2つに分かれます。

ユーザーが編集するコード

ユーザーが主に編集すべきコードです。
これらのコードを編集してDSPで信号処理を記述することが出来ます。

worker ディレクトリの中にDSPのコードがまとまっており、その中に userproc ディレクトリがあります。
ユーザが信号処理などを書くのは userproc ディレクトリ内のみで、その他は基本的に変更の必要はありません。

main.cpp には、起動処理~MainCPUとのデータ通信制御が書かれていますので変更しないで下さい。

Diagram
図 10. コード構成
main.cpp

起動処理やDSP通信処理などが書かれています。編集の必要は有りません。

userproc_command.h

DSPとの通信コマンドを定義するヘッダファイルです。
必要なパラメータはこのファイルに記載します。

userproc.h

ユーザーコードのヘッダファイルです。

userproc.cpp

ユーザーコード本体です。
このファイルに信号処理などを書く、または呼び出します。

ユーザーコードに用意されるインタフェース

userproc.cpp には、 Init , Exec , Flush , Set コマンドの枠組みが用意されており
ユーザーコードはそれぞれに対応する中身を書いていくことでDSP内での処理を実現することが出来ます。

ユーザーが記述すべき処理について説明します。
(※デフォルトではサンプルとしてRCフィルタが組み込まれています。)

コマンドによるDSP内部の状態遷移は下図の通り行われます。

Diagram
図 11. DSPの状態遷移

各コマンドを使い以下のような流れで処理を行います。

  1. AUDCMD_INIT_MICFRONTENDがコールされるとDSPが起動します。

  2. Init コマンドで必要なパラメータ(ch数やビット長など)を設定します。

  3. 記録開始するとキャプチャした音声データが Exec コマンドで定期的にDSPに送られるので所望のフィルタ処理をしてください。

  4. 任意のタイミングでDSP内部のパラメータなどを変更したい場合には、 Set コマンドを送ることで実装することを想定しています。このコマンドの実行タイミングは、 Exec を含めた受信順になります。+

  5. 記録停止すると最後の音声データの Exec の後、 Flush コマンドが送られるので終端処理の必要がある場合はここで処理をします。

コマンドの定義

各機能で使用するデータ型は userproc_command.h に書かれており、中身は自由に書き換えることが出来ます。

各コマンドのフォーマットは下図の様になっています。
先頭の白抜きの部分には最低限必要なパラメータが固定で配置されています。これらは変更しないで下さい。
ユーザーが userproc_command.h で定義するパラメータは下図の User param (ピンク色箇所)にあたる部分です。

Diagram
図 12. コマンドフォーマット

各コマンドについて説明します。

struct InitParam : public CustomprocCommand::CmdBase
  • Init処理用のパラメータです。
    デフォルトではすべてreserveとなっていますが、ch数やビット長など、必要なパラメータに変更してください。

struct ExecParam : public CustomprocCommand::CmdBase
  • Exec処理用のパラメータです。
    音声データのアドレス・サイズは上図の ExecParam にある様に継承元の CustomprocCommand::ExecParamBase に定義されています。
    詳細は /sdk/modules/include/audio/dsp_framework/customproc_command_base.h を参照してください。

struct FlushParam : public CustomprocCommand::CmdBase
  • Flush処理用のパラメータです。
    音声データのアドレス・サイズは上図の ExecParam にある様に継承元の CustomprocCommand::FlushParamBase に定義されています。
    詳細は /sdk/modules/include/audio/dsp_framework/customproc_command_base.h を参照してください。

struct SetParam : public CustomprocCommand::CmdBase
  • Set処理用のパラメータです。
    様々な動的に変更したいパラメータを定義します。デフォルトではRCフィルタのOn/Offや係数を定義しています。

各機能に対応する関数

下記の関数群は userproc.cpp に書かれています。中身は自由に書き換えることが出来ます。
それぞれのコマンド定義に従い処理を行います。

void UserProc::init(InitParam *)
  • InitParamに従い初期化を行う処理を書いて下さい。
    アプリケーションコードからの AUDCMD_INIT_PREPROCESS_DSP コマンドで実行されます。
    (デフォルトでは何もしていません。)

void UserProc::exec(ExecParam *)
  • ExecParamに従い信号処理を書いて下さい。
    記録を開始するとSDK内部から定期的に呼ばれます。
    1フレームは記録の設定がLPCMの場合には768サンプル、MP3の場合には1152(ただし16kHz時は1728)サンプルです。
    入力データアドレスからデータを取得し、信号処理を行い、出力データアドレスへ書き出して下さい。
    (デフォルトではRCフィルタ処理が書かれています。)

void UserProc::flush(FlushParam *)
  • FlushParamに従いFlush(終端)処理を書いて下さい。
    記録の停止時にSDK内部から一度だけ呼ばれます。
    IIRやFIRフィルタのように遅延が発生する場合は、最終フレームの後に flush を行い、遅延分の出力を行います。
    出力すべきデータがある場合には、出力データアドレスへ書き出して下さい。
    (デフォルトでは何もしていません。)

void UserProc::set(SetParam *)
  • SetParamに従い設定処理を書いてください。
    アプリケーションコードからの AUDCMD_SET_PREPROCESS_DSP コマンドで実行されます。
    (デフォルトではRCフィルタの係数を設定しています。)

5.2.3.2. Step2. PREPROC バイナリのビルド

ConfigurationPreprocess を有効にしていれば、本アプリケーションのビルド時に自動で PREPROC バイナリが作成されます。
作成されるパスは spresense/examples/audio_recorder/worker/ の下の PREPROC です。
これをSDカードの /mnt/sd0/BIN (PCから見ると \BIN )フォルダに置いてください。

5.3. Audio Recognizer サンプルアプリケーション

Audio Recognizer サンプルアプリケーションの動作について説明します。

5.3.1. ビルド&ロード手順

ここではコマンドラインによるビルド手順を示します。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/audio_recognizer を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/audio_recognizer
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
  4. Audio Recognizerの場合、音声認識処理を行うDSPバイナリのロードが必要です。 DSPバイナリの置き場所は、SDカードかSPI-Flashのどちらかを選択できますが、 ここでは、SDカードからロードする方法を行いますので、 "/mnt/sd0/BIN" に置きます。

    この /mnt/sd0/BIN は、SDカードをPCで読み込んだ時の、ルートディレクトリの下の BIN/ です。
    このディレクトリを作成し、ここに音声認識に必要なDSPを置きます。

    本サンプルアプリケーションでは、音声認識のDSPバイナリは、ユーザーカスタムのものを使用する設定となっており、バイナリは spresense/examples/audio_recognizer/worker_recognizer/ の下に RCGPROC として生成されます。認識処理をカスタムする場合には、RecognizerPROCのカスタムについてを参照してください。

5.3.2. Audio Recognizerの動作確認

ビルドした nuttx.spk を実機へロードするとAudio Recognizerのプログラムが実行されます。

Helloサンプルと同様に、シリアルターミナルを開きます。

minicom -D /dev/ttyUSB0 -b 115200 -s

builtinされている、 audio_recognizer アプリを実行すると、

tutorial recognizer log
図 13. 音声認識動作時ログ

というログが表示され、音声認識が開始されます。

認識結果はアプリケーションコード( audio_recognizer_main.cpp )中にあるコールバック関数で受け取っています。
受け取るパラメータの構成は音声認識用DSPで決めています。 RecognizerPROC を参照して下さい。
データはMemoryHandleで受信していますので、下記の様に、そのアドレスを参照することでデータを取得することが出来ます。

static void recognizer_find_callback(AsRecognitionInfo info)
{
  /* Get Recognition result */

  MyRecognizerResultFormat *result =
    static_cast<MyRecognizerResultFormat *>(info.getVa())

  /* Print result */
  ...
  printf("Data size %d byte\n", info.size);
  printf("Data1 : %x, Data2 : %x\n", result->data1, result->data2);
  ...
}

エラーが発生した場合は、オーディオサブシステムのエラーについて を参照してください。

5.3.3. 付録: 独自の信号処理を行う

これまでのチュートリアルで、Recognizerとして十分な機能を実現することができました。
ここからは、より高度な処理を行う方法を説明していきます。

キャプチャした音声はサンプリング周波数が48kHzまたは192kHz、ビット長が16bitまたは32bitです。
Audio Recognizerではこれに対して、認識ライブラリの入力フォーマットに合わせ前処理を行うことが出来ます。
これを行いたい場合にはコンフィグメニューで [Use preprocess] を有効にする必要があります。

  1. Preprocessを有効にします

    コンフィグメニューを開きます。

    tools/cofig.py -m
    

    [Use preprocess] にチェックを入れます。

    [Examples]
      [Audio recognizer example]
        [Use preprocess]        <= Y
    Preprocessの詳細についてはSDKデベロッパーガイドの Set preprocess を参照して下さい。
  2. ビルドを行います。

    make
    

    全ての手順がうまくいけば、 PREPROC バイナリが spresense/examples/audio_recognizer/worker_preprocess/ の下に作成されます。
    これをSDカードの /mnt/sd0/BIN (PCから見ると BIN/ )フォルダに置いてください。

    本サンプルアプリケーションでは、 PREPROC には簡単なRCfilterがデフォルトで組み込まれています。
    独自の信号処理などにカスタムをしたい場合には こちら を参考にして下さい。

5.3.4. DSPバイナリのカスタムについて

audio_recognizerサンプルではPre処理用(以下 PREPROC )、認識処理用(以下 RCGPROC )の2つのDSPバイナリを使用します。
それらのカスタム方法について説明します。

5.3.4.1. Step1. PREPROC, RCGPROCのコード編集

PREPROC, RCGPROC のコード構成と編集箇所について説明します。
コードは、ユーザー編集すべき部分とフレームワークとして提供される部分の2つに分かれます。

ユーザーが編集するコード

ユーザーが主に編集すべきコードです。
これらのコードを編集してDSPで信号処理や認識処理を記述することが出来ます。

PREPROCworker_preprocess ディレクトリの中に、 RCGPROCworker_recognizer の中にDSPのコードがまとまっており、それぞれその中に userproc ディレクトリがあります。
ユーザが信号・認識処理などを書くのは userproc ディレクトリ内のみで、その他は基本的に変更の必要はありません。

main.cpp には、起動処理~MainCPU(Supervisor)とのデータ通信制御が書かれていますので変更しないで下さい。

Diagram
図 14. PREPROC, RCGPROCコード構成
main.cpp

起動処理やDSP通信処理などが書かれています。編集の必要は有りません。

userproc_command.h

Pre処理用DSPとの通信コマンドを定義するヘッダファイルです。
必要なパラメータはこのファイルに記載します。

userproc.h

Pre処理用DSPのユーザーコードのヘッダファイルです。

userproc.cpp

Pre処理用DSPのユーザーコード本体です。
このファイルに信号処理などを書く、または呼び出します。

rcgproc_command.h

認識用DSPとの通信コマンドを定義するヘッダファイルです。
必要なパラメータはこのファイルに記載します。

rcgproc.h

認識用DSPのユーザーコードのヘッダファイルです。

rcgproc.cpp

認識用DSPのユーザーコード本体です。
このファイルに認識処理を書く、または呼び出します。

ユーザーコードに用意されるインタフェース

Init , Exec , Flush , Set コマンドの枠組みが用意されており
ユーザーコードはそれぞれに対応する中身を書いていくことでDSP内での処理を実現することが出来ます。

ユーザーが記述すべき処理について説明します。
(※デフォルトではサンプルとして PREPROC にはRCフィルタが組み込まれています。)

コマンドによるDSP内部の状態遷移は下図の通り行われます。

Diagram
図 15. DSPの状態遷移

各コマンドを使い以下のような流れで処理を行います。

  1. AUDCMD_INIT_RECOGNIZERがコールされると認識用DSPが起動します。

  2. Init コマンドで必要なパラメータ(ch数やビット長など)を設定します。

  3. 認識処理を開始するとキャプチャした音声データが Exec コマンドで定期的にDSPに送られるので所望の認識処理をしてください。

  4. 任意のタイミングでDSP内部のパラメータなどを変更したい場合には、 Set コマンドを送ることで実装することを想定しています。このコマンドの実行タイミングは、 Exec を含めた受信順になります。+

  5. 認識処理を停止すると最後の音声データの Exec の後、 Flush コマンドが送られるので終端処理の必要がある場合はここで処理をします。

コマンドの定義

各機能で使用するデータ型は、 PREPROC では userproc_command.h に、 RCGPROC では rcgproc_command.h に書かれており、中身は自由に書き換えることが出来ます。

各コマンドのフォーマットは下図の様になっており、これは PREPROC RCGPROC で同じです。
先頭の白抜きの部分には最低限必要なパラメータが固定で配置されています。これらは変更しないで下さい。
ユーザーが userproc_command.h で定義するパラメータは下図の User param (ピンク色箇所)にあたる部分です。

notificationExec コマンドの応答(認識結果)フラグで、0以外の指定でアプリケーションまで応答を戻します。
例えば、通常は応答を戻さず認識結果の変化点(認識無し→有りになった点)などでのみ応答したい場合などに使用出来ます。

Diagram
図 16. コマンドフォーマット

各コマンドについて説明します。

struct InitRcgParam : public CustomprocCommand::CmdBase
  • Init処理用のパラメータです。
    デフォルトではch数とビット幅が設定されています。必要なパラメータに変更してください。

struct ExecRcgParam : public CustomprocCommand::CmdBase
  • Exec処理用のパラメータです。
    音声データのアドレス・サイズは上図の ExecParam にある様に継承元の CustomprocCommand::ExecParamBase に定義されています。
    詳細は /sdk/modules/include/audio/dsp_framework/customproc_command_base.h を参照してください。

struct FlushRcgParam : public CustomprocCommand::CmdBase
  • Flush処理用のパラメータです。
    音声データのアドレス・サイズは上図の FlushParam にある様に継承元の CustomprocCommand::FlushParamBase に定義されています。
    詳細は /sdk/modules/include/audio/dsp_framework/customproc_command_base.h を参照してください。

struct SetRcgParam : public CustomprocCommand::CmdBase
  • Set処理用のパラメータです。
    様々な動的に変更したいパラメータを定義します。デフォルトでは認識処理のOn/Offを定義しています。

各機能に対応する関数

下記の関数群は rcgproc.cpp に書かれています。中身は自由に書き換えることが出来ます。
それぞれのコマンド定義に従い処理を行います。

void RcgProc::init(InitRcgParam *)
  • InitRcgParamに従い初期化を行う処理を書いて下さい。
    アプリケーションコードからの AUDCMD_INIT_RECOGNIZER_DSP コマンドで実行されます。
    (デフォルトではCh数とビット幅の設定をしています。)

void RcgProc::exec(ExecRcgParam *)
  • ExecRcgParamに従い信号処理を書いて下さい。
    認識処理を開始するとSDK内部から定期的に呼ばれます。
    当サンプルアプリケーションでは1フレームは320サンプルです(アプリケーションで自由に変更できます)。
    入力データアドレスからデータを取得し、認識処理を行い、結果を出力データアドレスへ書き出して下さい。
    (デフォルトでは入力フレーム内サンプル値の最大・最少・平均値を出力しています。)

void RcgProc::flush(FlushRcgParam *)
  • FlushRcgParamに従いFlush(終端)処理を書いて下さい。
    認識処理の停止時にSDK内部から一度だけ呼ばれます。
    認識処理にフレーム遅延が発生する場合は、最終フレームの後に flush を行い遅延分の出力を行います。
    出力すべきデータがある場合には、出力データアドレスへ書き出して下さい。
    (デフォルトでは何もしていません。)

void RcgProc::set(SetRcgParam *)
  • SetRcgParamに従い設定処理を書いてください。
    アプリケーションコードからの AUDCMD_SET_RECOGNIZER_DSP コマンドで実行されます。
    (デフォルトでは認識処理のOn/Offを設定しています。)

5.3.4.2. Step2. PREPROC , RCGPROC バイナリのビルド

本アプリケーションのビルド時に自動で RCGPROC バイナリが作成されます。
また、 ConfigurationPreprocess を有効にしていれば PREPROC バイナリも作成されます。
作成されるパスは worker_recognizer/ の下に RCGPROCworker_preprocess/ の下に PREPROC です。
これをSDカードの /mnt/sd0/BIN (PCから見ると \BIN )フォルダに置いてください。

6. Camera チュートリアル

6.1. camera サンプルアプリケーション

この章では、Spresense Cameraボードを用いたサンプルについて解説します。
このサンプルでは、Spresense Cameraの基本的な使い方を体験していただくために用意しています。

6.1.1. 動作環境

このサンプルを動作させるには、以下のハードウェアを使う前提となっています。

  • Spresense Main Board

  • Spresense Camera Board

  • Spresense Extension Board

  • Arduino UNO LCD Connector board

  • ILI9341 2.2inch LCD

6.1.2. ソースコード

このサンプルのソースコードは、 examples/camera 以下にあります。

ディレクトリの中のファイル構成は以下のようになります。

camera/

.
├── Kconfig
├── Make.defs
├── Makefile
├── README.txt
├── camera_bkgd.c
├── camera_bkgd.h
├── camera_fileutil.c
├── camera_fileutil.h
└── camera_main.c

主要なファイル・フォルダの概略は以下のようになります。

ファイル・フォルダ名 概要

camera_main.c

main()関数が実装されているファイルです。

camera_bkgd.c

NuttXのグラフィクスシステムであるNXを制御するためのユーティリティ関数の実装がされているファイルです。

camera_fileutil.c

イメージセンサから取得したデータをファイルに保存するためのユーティリティ関数が実装されているファイルです。

6.1.3. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/camera を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/camera
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

6.1.4. 動作確認

シリアルターミナルを開いて、camera コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から camera コマンドを実行します。

    nsh>プロンプトからcameraと入力してEnterを押し、実行します。

    nsh> camera

    正しく動作すれば、LCDにカメラの画像が表示されます。
    なお、デフォルトでは10フレーム表示したのち、終了するようになっています。
    カメラの画像を永遠に表示するためには、引数に0を入れて、以下のようにコマンドを実行してください。

    nsh> camera 0

6.2. multiwebcam サンプルアプリケーション

この章では、multiwebcamに関するサンプルアプリケーションの動作手順を示します。 このサンプルは、IDY Spresense Wi-Fi Add-onボードiS110Bを用いて、Cameraで撮ったJPEG画像をWi-Fiを介して接続先のデバイスに送るサンプルになります。 このサンプルには2つのモードが存在します。

  1. 1対1の通信で、Motion JPEG over HTTPを転送プロトコルとして用いるモード (ブラウザからSpresenseにアクセスするとカメラ画像をモニターできます)

  2. 1対多の通信で、独自の転送プロトコルを用いて複数のSpresenseから画像データを取得してPCアプリで表示させるモード (特殊なアプリが必要になりますが、複数のカメラ画像を一画面で見ることができます。)

どちらの場合も、Spresenseは画像を送信するサーバとして動作し、ブラウザもしくはPCツールからサーバに接続して画像を取得することになります。

このサンプルは、以下の技術要素をもとに実装されています。

  • Spresense Camera (V4L2 like I/F)

  • multi pthread programing

  • socket programing

  • Tiny HTTP server

6.2.1. 動作環境

このサンプルを動作させるには、以下のハードウェアを使う前提となっています。

  • Spresense Main Board

  • Spresense Camera Board

  • IDY Wi-Fi Add-on Board iS110B

6.2.2. ソースコード

このサンプルのソースコードは、 examples/multi_webcamera 以下にあります。

ディレクトリの中のファイル構成は以下のようになります。

multi_wabcamera/

 ├── Kconfig
 ├── Make.defs
 ├── Makefile
 ├── README.txt
 ├── multiwebcam_main.c
 ├── multiwebcam_perf.h
 ├── multiwebcam_server.c
 ├── multiwebcam_server.h
 ├── multiwebcam_threads.c
 ├── multiwebcam_threads.h
 ├── multiwebcam_util.c
 ├── multiwebcam_util.h
 ├── startup_script/
 │   └── init.rc
 └── host/
     ├── ImgScaler.py
     ├── MultiCameraFrame.py
     ├── NetImgReceiver.py
     └── test_module/

主要なファイル・フォルダの概略は以下のようになります。

ファイル・フォルダ名 概要

multiwebcam_main.c

サンプルコードのmain処理の実装ファイル

multiwebcam_server.c

ネットワーク処理の実装ファイル

multiwebcam_threads.c

イメージセンサーからのJPEGデータを取得するThreadおよび取得したJPEGデータを接続されたクライアントに送信するThreadの実装ファイル

multiwebcam_util.c

Thread間でのメッセージ送受信用のQueueの実装ファイル

startup_script/init.rc

Spresense起動用サンプル(テンプレート)スクリプト

host/

マルチカメラモードの場合のPC側のサンプルコード(Python)

6.2.3. ソースコードの解説

この節では、ソースコードの動作内容について解説します。

アプリの全体の概要は以下の図のようになります。

multiwebcam overview

このアプリでは、main()、camera_thread()、jpeg_sender()の3つのThreadが協調して動作しています。 次に3つのそれぞれの動作を解説します。

6.2.3.1. main()

メイン関数。
SpresenseのVideoドライバの初期化を行い(イメージセンサーの初期化含む)、camera_thread()をThreadとして起動します。(上図の緑のブロックの①)
その後、サーバとしてのソケットを生成し accept() 関数を呼び出してクライアントからの接続を待ちます。 (上図の緑のブロックの②)
クライアントから接続されると、JPEGを送信するためのThreadとしてjpeg_sender()を起動します。(上図の緑のブロックの③)
jpeg_sender()起動後、main()内では、jpeg_sender() Threadの終了を待ち、終了したら、再度 accept() にて新たなクライアントからの接続を待ちます。 (上図の緑のブロックの③)

ポイントとなるステップとソースコードを以下に抜粋します。

緑ブロックの番号 ファイル名:行番号 コード 解説

multiwebcam_main.c:77

video_initialize(VIDEO_DEV_PATH);

videoドライバ初期化をしています。

multiwebcam_main.c:79

v_fd = open(VIDEO_DEV_PATH, 0);

Videoドライバデバイスファイルのオープンをします。

multiwebcam_main.c:86

ret = multiwebcam_prepare_camera_buf(v_fd,
V4L2_BUF_TYPE_STILL_CAPTURE,
V4L2_BUF_MODE_RING, 2, &vbuffs);

JPEGデータのバッファの作成とVideoドライバへの登録

multiwebcam_main.c:105

rsock = multiwebcam_initserver(MULTIWEBCAM_PORT_NO /* Port Number */);

サーバソケットを作成しています。

multiwebcam_main.c:109

cam_thd = multiwebcam_start_camerathread(v_fd);

camera_thread() Threadの生成

multiwebcam_main.c:117

wsock = multiwebcam_waitconnection(rsock, &client);

クライアントからの接続待ち、具体的にはaccept()関数を読んで接続待ちをしています。

multiwebcam_main.c:122

jpeg_thd = multiwebcam_start_jpegsender(wsock);

jpeg_sender() Threadの起動を行なっています。

multiwebcam_main.c:123

pthread_join(jpeg_thd, NULL);

jpeg_sender() Threadの終了待ち

Thread終了後、②に戻ります。

6.2.3.2. camera_thread()

イメージセンサーから画像を取得するためのThread。
main()関数から起動されると、Videoドライバに対して、VIDIOC_DQBUFを発行してキャプチャしたデータを取得します。 (上図の青のブロックの①)
その後、jpeg_sender() Threadが起動しているかを確認して、起動していたら、取得したJPEGデータを、jpeg_sender() Threadに渡すために、action_queueにデータを送ります。(上図の青のブロックの②)
データ送信後にempty_queue()から空のバッファを取得します。(上図の青のブロックの③)
その後、VideoドライバにVIDIOC_QBUFで空のバッファをセットして、イメージセンサーからのJPEGデータの取得を待ちます。(上図の青のブロックの④)
あとは、これを繰り返します。

ポイントとなるステップとソースコードを以下に抜粋します。

青ブロックの番号 ファイル名:行番号 コード 解説

multiwebcam_thread.c:72

multiwebcam_get_picture_buf(v_fd, &buf, V4L2_BUF_TYPE_STILL_CAPTURE);

VideoドライバからJPEGイメージを取得します。

multiwebcam_thread.c:78

while (!is_run){ …​ }

jpeg_sender() Thread起動待ち

multiwebcam_thread.c:93

multiwebcam_push_action(multiwebcam_get_vbuffer(&buf));

action_queueに読み出したJPEGデータをプッシュします。

multiwebcam_thread.c:98

while (multiwebcam_is_emptyqueue_empty() && is_run){ …​ }

empty_queueに使い終わったバッファがプッシュされるまで待ちます。

multiwebcam_thread.c:98

for (vbuf = multiwebcam_pull_empty(); vbuf != NULL; vbuf = multiwebcam_pull_empty()){ …​ }

empty_queueに入っているバッファ全てをVideoドライバに再登録しています。

Videoドライバに再登録が終わると、①に戻ります。

6.2.3.3. jpeg_sender()

クライアントからの接続が確立するとJPEGデータを送信するために動作するThread。
クライアントからの接続が確立したら、main() Threadから起動され、camera_thread()に起動した旨を伝えるために、is_run変数をtrueにします。
その後、action_queueにcamera_thread()がJPEGデータをプッシュするのを待ち、プッシュされたら取り出します。(上図の黄色のブロックの①)
データを取得したら、現在の選択されているプロトコル(Motion JPEG over HTTP、もしくは、独自転送プロトコル)にしたがって、JPEGデータをクライアントに送信します。(上図の黄色のブロックの②)
送信完了後、empty_queueに使い終わったJPEGデータのバッファを送ります。(上図の黄色のブロックの③)
なお、クライアントとの接続が切れた場合、camera_thread()に対して終了を伝えるためis_runをfalseにして、Threadを終了します。(上図の黄色のブロックから緑のブロックに向かっている点線④)

ポイントとなるステップとソースコードを以下に抜粋します。

黄色ブロックの番号 ファイル名:行番号 コード 解説

multiwebcam_thread.c:145

is_run = true;

camera_thread() Threadに起動を知らせます。

multiwebcam_thread.c:149

multiwabcam_sendheader(sock);

接続の最初の1回だけ送信されるべきデータを送ります。
具体的にはHTTPのレスポンスヘッダを送信します。(Motion JPEG over HTTPの場合のみ)

multiwebcam_thread.c:155

while(multiwebcam_is_actionqueue_empty()){ …​ }

action_queueが空の間(camera_thread()がJPEGをプッシュするまでの間)待ちます。

multiwebcam_thread.c:159

buf = multiwebcam_pull_action();

action_queueからJPEGデータを取り出します。

multiwebcam_thread.c:162

ret = multiwebcam_sendframe(sock, (char *)buf→start, (int)buf→jpg_len);

取り出したデータをクライアントに送信します。

multiwebcam_thread.c:166

multiwebcam_push_empty(buf);

送信し終わったバッファをempty_queueにプッシュします。

empty_queueにプッシュした後、①に戻ります。

6.2.4. ビルドおよび実行手順(1対1のMotion JPEG over HTTPモードの場合)

このサンプルコードは、1対1のMotion JPEG over HTTPで送信するモードと、多対1の独自転送プロトコルで送信するモードの2つのモードが存在します。
この節では、1対1のMotion JPEG over HTTPを用いた通信をさせる場合について解説します。
多対1の独自転送プロトコルモードでの通信を行う場合は、 ビルドおよび実行手順(多対1の独自転送プロトコルモード) を参照してください。

6.2.4.1. ビルド手順

ここでは、CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/multiwebcam を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/multiwebcam
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    ビルドが無事に完了すると sdk フォルダの中に nuttx.spk というファイルが生成されるので、これをターゲットのSpresenseに書き込みます。 この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
6.2.4.2. 動作確認

実機側のサンプルアプリを起動するために、シリアルターミナルを開いて、Wi-Fiに接続し、 multiwebcam コマンドを実行します。

  1. シリアルターミナルを起動します。

    まず、ターミナルソフトから、Spresenseのシリアルポートに接続して、NuttShellに入ります。
    以下の例では、ターゲットのSpresenseと繋がっているシリアルポートは /dev/ttyUSB0 を、baudrate として 115200 bps を設定してminicomで接続しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Wi-Fi の接続を行います。

    下記の例では、APモードでWi-Fiを起動し、SSIDをspresense_net、パスワードを0123456789としています。
    Wi-Fiモジュールが正しく起動されると、IPアドレスが設定されるので、それを確認します。 下記の例では、Wi-Fiモジュールの起動が完了するまで、5秒ほど sleep が入っています。
    ifconfigコマンドで、下記のように、IPアドレスが割り当てられます。

    nsh> gs2200m -a 1 spresense_net 0123456789 &
    nsh> sleep 5
    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 3c:95:09:00:56:49 at UP
            inet addr:192.168.11.1 DRaddr:192.168.11.1 Mask:255.255.255.0

    上記の例では、 192.168.11.1 が Spresense のIPアドレスになります。
    なお、HWaddrは購入されたWi-Fiモジュールによって変わる場合があります。

  3. アプリを起動します。

    Wi-Fi の設定が完了したら、 multiwebcam アプリを起動します。

    nsh> multiwebcam

    これで、Spresense 側の準備は完了です。

  4. PCやスマホで、作成したWi-Fiのネットワークに接続します。

    次に、PCやスマホなどのブラウザでカメラからの画像を見るために、まずは先ほど Spresense で起動した Wi-Fi のネットワークにPCやスマホを接続します。 スマホなどで、Wi-Fiのネットワークに接続します。
    接続するWi-FiのSSIDは、先ほど設定した、 spresense_net になります。 PCやスマホの Wi-Fi 接続設定で、 spresense_net を探し、接続を行います。
    接続する際の暗号方式は、 WPA/WPA2 を選択してください。
    パスワードは、上記で設定した、 0123456789 です。

  5. ブラウザで Spresense カメラに接続して Live View を表示します。

    無事に Wi-Fi ネットワークに接続ができたら、接続したPCやスマホのブラウザを開き、URL入力欄に、以下のURLを入力することで、カメラの画像を表示させることができます。

    http://192.168.11.1

    正しく動作すれば、ブラウザ上にカメラの画像が表示されます。

6.2.5. ビルドおよび実行手順(多対1の独自転送プロトコルモード)

続いて多対1の独自プロトコルを用いた場合のビルド方法および実行方法について説明します。
基本的にはMotion JPEG over HTTPとビルド方法は同じです。
ビルド方法の唯一の違いはExampleのConfigメニューにある、"Http MJPEG is used"オプションを無効にすることです。
実機側の使い方についてもほぼ同じで、唯一の違いはWiFiの接続設定をアクセスポイントではなく、ステーションにすることです。
では、ステップごとに説明します。

6.2.5.1. ビルド手順

ここでは、CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションを行います。

    引数に examples/multiwebcam を指定してコンフィグレーションを実行します。

    make distclean
    tools/config.py examples/multiwebcam
    

    その後、以下のコマンドでmenu configを開きます。

    make menuconfig
    

    メニューを開いたら、矢印キーでメニューの一番下にある、"Application Configuration"まで移動してEnterキーを押して中のメニューに入ります。Application Configurationの中にある、"Spresense SDK"メニューに同様にして入り、さらに"Examples"に進みます。

      "Application Configuration"
           -> "Spresense SDK"
                 -> "Examples"

    Examplesに入ると、 ”[*] Multi Web Camera" というチェックが付いた項目の中に "[*] Http MJPEG is used" という項目があるので、この項目にカーソルを合わせてスペースキーでチェックを外します。

    multiwebcam config

    チェックを外したら、"<Exit>"にカーソルを合わせてエンターを押して行き、一番上の階層で<Exit>をすると、"Do you wish to save your new configuration?"と聞かれるので、<Yes>を選択してコンフィグの変更をセーブしてmenuconfigを終了します。

  3. ビルドを行います。

    コンフィグが完了したら、以下のコマンドでビルドを行います。

    make
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

  4. nuttx.spk を Spresense ボードへ書き込みます。

    ビルドが無事に完了すると sdk フォルダの中に nuttx.spk というファイルが生成されるので、これをターゲットのSpresenseに書き込みます。 この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
6.2.5.2. 動作確認

実機側のサンプルアプリを起動するために、シリアルターミナルを開いて、Wi-Fiに接続し、 multiwebcam コマンドを実行します。

  1. シリアルターミナルを起動します。

    まず、ターミナルソフトから、Spresenseのシリアルポートに接続して、NuttShellに入ります。
    以下の例では、ターゲットのSpresenseと繋がっているシリアルポートは /dev/ttyUSB0 を、baudrate として 115200 bps を設定してminicomで接続しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Wi-Fi の接続を行います。

    下記の例では、STAモードでWi-Fiを起動します。STAモードのため、既存のWi-Fiネットワークに接続を行うことになるので、予め接続するWi-FiネットワークのSSIDとパスワードを準備してください。
    この例では、SSIDを hogehoge、パスワードを hogehoge1 として説明しています。
    Wi-Fiモジュールが正しく起動されると、IPアドレスが設定されるので、それを確認します。 下記の例では、Wi-Fiモジュールの起動が完了するまで、5秒ほど sleep が入っています。
    ifconfigコマンドで、下記のように、IPアドレスが割り当てられます。

    nsh> gs2200m hogehoge hogehoge1 &
    nsh> sleep 5
    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 3c:95:09:00:56:49 at UP
            inet addr:XXX.XXX.XXX.XXX DRaddr:XXX.XXX.XXX.XXX Mask:255.255.255.0

    ifconfigの結果、inet addrに接続された結果としてのIPアドレスが表示されます。
    なお、HWaddrは購入されたWi-Fiモジュールによって変わる場合があります。
    ここで取得したIPアドレスはPCアプリの設定で必要になるのでメモしておいてください。

  3. アプリを起動します。

    Wi-Fi の設定が完了したら、 multiwebcam アプリを起動します。

    nsh> multiwebcam

    これで、Spresense 側の準備は完了です。

    複数のSpresenseを用いる場合は同様にWi-Fiの接続とmultiwebcameraの起動を行なってください。

  4. PCアプリの起動

    PC側のアプリは複数のカメラの画像を表示するために、参考としてPythonで作成されたアプリになっています。このアプリはLinux PCで動作することを確認しています。

    実行するには、まずPython 2.7のインストールをしてください。
    さらに、Pythonの以下のライブラリが必要になりますので、それぞれインストールをしてください。

    • python-wxgtk3.0

    • python-wxtools

    Linuxでは以下のコマンドでインストールが行えます。

    $ sudo apt install python-wxgtk3.0  python-wxtools
    

    必要なPythonライブラリのインストールが完了したら、まず、接続するSpresenseのIPアドレスを設定します。先ほど実機を起動した際にメモしたIPアドレスを用います。

    host/ フォルダの中に移動して、MultiCameraFrame.pyを開きます。
    そのファイルの183行目に記載されている、

            servers = ( ('192.168.11.1', 10080),
                        ('192.168.11.2', 10080),
                        ('192.168.11.3', 10080),
                        ('192.168.11.4', 10080) )

    というコードの192.168.11.1 ~ 4と記載されているIPアドレスを、メモしたIPアドレスに書き換えてください。
    デバイスが4つまで表示が可能ですが、4つない場合は、持っている数分だけ変更し、残りはそのままで問題ありません。
    例としてデバイスが2つで、それぞれ、10.0.0.5、10.0.0.8だった場合

            servers = ( ('10.0.0.5', 10080),
                        ('10.0.0.8', 10080),
                        ('192.168.11.3', 10080),
                        ('192.168.11.4', 10080) )

    となります。
    編集が終わったらファイルをセーブしてコマンドプロンプトから以下のコマンドを叩くことで、アプリを起動させることが出来ます。

    python MultiCameraFrame.py
    

    正しく起動すると、全画面にWindowが表示され、デバイスからの画像の表示が始まります。

    なお、当然ながら、PCはデバイスが接続したものと同じWi-Fiネットワークに接続しておく必要があります。

6.2.5.3. PC側の側アプリの解説

ここでは、PCアプリの簡単な解説を行います。
まず、PCアプリの構造を以下の図に示します。

multiwebcam pcapp

ホストアプリは、WindowプログラミングのフレームワークとしてwxPythonを利用しています。MultiCameraFrame.pyに実装されているカスタムパネル:WebCamPanelを持つMultiCamFrameを生成します。
その引数に接続するSpresenseのIPアドレスとポート番号のリストを渡します。
Frameが起動すると、WebCamPanelを生成し、その中で4つのStaticBitmapを生成してフレームに貼り、その後、渡されたIPアドレスとポート番号のリストを元に、4つのNetImgReceiverを生成します。NetImgReceiverには、NetImgReceiver.pyに実装しており、接続するサーバのIPアドレスとポート番号及びStaticBitmapに紐づくID番号が付与されます。
NetImgReceiverは生成された後、receiveThread()がThreadとして起動し、並列動作を始めます。
receiveThread()では、指定されたserver IPとポート番号に基づいて、Spresense デバイス側のサーバに対して接続要求を行います。
接続が確立すると、JPEGの画像の受信を開始します。
JPEGの受信が完了するとWebCamPanelのPictUpdateEventを発生させ、受信したJPEGデータをID番号と共にWebCamPanelに送ります。
WebCamPanel内では、PictUpdateEventが発生するとonPictUpdate()がコールバックされます。この関数では、受信したJPEGデータとID番号を受け取り、画像サイズの調整をしたのち、ID番号に基づいてStaticBitmapに受信した画像を表示します。

6.2.6. Appendix : パケットフォーマット

ここでは、各モードでやりとりされるパケットのフォーマットについて解説します。

6.2.6.1. Motion JPEG over HTTP

こちらは、HTMLの規格として定義されている、”multipart/x-mixed-replace"を用いています。詳しくは以下を参照してください。
https://html.spec.whatwg.org/#read-multipart-x-mixed-replace

6.2.6.2. 独自プロトコル

独自プロトコルは、シンプルに接続されたクライアントに対してただひたすらJPEGデータを送るだけ、という目的に作成されたプロトコルになります。
パケットには、デリミタとして、4文字のAsciiコード "SZ: " を先頭に、 4Byte(Little endian)のJPEGのデータサイズ(Byte数)、その後ろに、そのサイズ分のJPEGデータ、という並びになります。
上記のフォーマットを図で表すと以下のようになります。

multiwebcam pktfmt

6.2.7. Tips : Spresense カメラの自動起動

Spresenseのnshプロンプトで実行しているコマンドは、起動時に自動的に実行することも出来ます。
詳しくは、 アプリケーションの自動起動方法 を参照してください。

7. JPEG チュートリアル

7.1. JPEG デコード サンプルアプリケーション

7.1.1. 概要

本サンプルアプリケーションは、JPEGファイルをデコードして、YUV4:2:2フォーマットのファイルを生成するアプリケーションになります。

7.1.1.1. 動作環境
  • Spresense メインボード

  • JPEGファイル

7.1.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/jpeg_decode を指定してコンフィグレーションを実行します。

    tools/config.py examples/jpeg_decode
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

7.1.3. 動作確認

シリアルターミナルを開いて、jpeg_decode コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から jpeg_decode コマンドを実行します。

    jpeg_decode コマンドの使い方を以下に示します。

    nsh> jpeg_decode <filename>
    filename

    デコードしたいJPEGファイルをフルパスで指定します。

    filenameで指定するファイルを /mnt/spif/ もしくは /mnt/sd0/ に置いて実行してください。 デコード結果は、同じディレクトリに拡張子 .YUVで生成されます。

本サンプルアプリケーションはfilenameで指定されたファイルをデコードして、 デコード元と同じディレクトリにデコード結果を保存します。 デコード結果は、デコード元のファイルの拡張子を.YUVに変更したものになります。

jpeg_decode コマンドを実行してSAMPLE.JPGというJPEGファイルを デコードした例を以下に示します。

nsh> jpeg_decode /mnt/sd0/SAMPLE.JPG
Decode result is saved in /mnt/sd0/SAMPLE.YUV.

また、本サンプルアプリケーションはデコード結果をファイルに保存しますが、
jpeg_decode_main.cに定義されているput_scanline_someplace()関数を編集することで、 LCDに描画するなど、ファイル以外の形式に結果を出力するようにすることもできます。

8. LTE チュートリアル

8.1. LTE HTTP GET サンプルアプリケーション

8.1.1. 概要

本サンプルプログラムは、LTE通信機能を用いて、HTTP GETを行うサンプルです。

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

8.1.1.1. 動作環境
  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

  • microSD カード

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

本サンプルプログラムではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

8.1.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションを行います。

    引数に examples/lte_http_get を指定してコンフィグレーションを実行します。

    tools/config.py examples/lte_http_get
    

    サンプルアプリケーションのコンフィグレーションを変更します。

    tools/config.py -m
    

    APNのパラメータを設定します。(ご使用のSIMに合わせて設定してください。)

    Application Configuration -> Spresense SDK -> Examples -> HTTP GET method using LTE example
    - Access Point Name (CONFIG_EXAMPLES_LTE_HTTP_GET_APN_NAME)
    - IP type Selection
    - Authentication type Selection
    - Username used for authentication (CONFIG_EXAMPLES_LTE_HTTP_GET_APN_USERNAME)
    - Password used for authentication (CONFIG_EXAMPLES_LTE_HTTP_GET_APN_PASSWD)
    tutorial lte http get apn
    コンフィグレーション名 説明

    Access Point Name

    アクセスポイント名

    IP type Selection

    APNプロトコル。IPv4IPv6IPv4/v6 から選択します。

    Authentication type Selection

    認証タイプ。 NonePAPCHAP から選択します。

    Username used for authentication

    ユーザ名。 認証タイプに None を選択した場合、設定は無視されます。

    Password used for authentication

    パスワード。 認証タイプに None を選択した場合、設定は無視されます。

  3. ビルドを実行します。 make コマンドを実行しビルドします。

    make
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

  4. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

8.1.3. 動作確認

シリアルターミナルを開いて、lte_http_get コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から lte_http_get コマンドを実行します。

    lte_http_get コマンドの使い方を以下に示します。

    nsh> lte_http_get <url>
    url

    インターネット上に置かれているファイルのURLを指定します。 http:// もしくは https:// から始まる必要があります。urlを指定しない場合、 http://example.com/index.html となります。

本サンプルアプリケーションはurlに指定されたファイルをダウンロードしシリアルターミナルに出力します。

lte_http_get コマンドを実行した例を以下に示します。

nsh> lte_http_get
app_restart_cb called. reason:Modem restart by application.
pdn.session_id : 1
pdn.active     : 1
pdn.apn_type   : 0x202
pdn.ipaddr[0].addr : 10.212.60.255
app_localtime_report_cb called: localtime : "19/12/06 : 18:24:33"
set localtime completed: 2019/12/06,18:24:33
<!doctype html>
<html>
<head>
    <title>Example Domain</title>

    <meta charset="utf-8" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style type="text/css">
    body {
        background-color: #f0f0f2;
        margin: 0;
        padding: 0;
        font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;

    }
    div {
        width: 600px;
        margin: 5em auto;
        padding: 2em;
        background-color: #fdfdff;
        border-radius: 0.5em;
        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
    }
    a:link, a:visited {
        color: #38488f;
        text-decoration: none;
    }
    @media (max-width: 700px) {
        div {
            margin: 0 auto;
            width: auto;
        }
    }
    </style>
</head>

<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.</p>
    <p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
nsh>

<url>https:// から始まるセキュリティで保護されたページを指定した際、 pdn.session_id などのLTEネットワーク接続状況が表示された後、数分立っても応答がない場合は、お使いのSpresense LTE拡張ボードのファームウェアが古い可能性があります。
Updateツール を参照の上、最新のファームウェアへアップデートしてください。

本サンプルアプリケーションの、wgetwget_post に変更することによりHTTPのPOSTメソッドが使用できます。
以下の例を参考に wget_post の第2引数にPOSTするデータを設定してください。

wget_post(url, "Hello spresense world!!" ,g_app_iobuffer, APP_IOBUFFER_LEN, app_wget_cb, NULL);

また、本サンプルアプリケーションはダウンロードしたファイルをシリアルターミナルに出力しますが、
以下の様に app_wget_cb() 関数を一部変更することにより、ファイルに保存することができます。

static void app_wget_cb(FAR char **buffer, int offset, int datend,
                        FAR int *buflen, FAR void *arg)
{
  int fd;

  fd = open("/mnt/spif/index.html", (O_WRONLY | O_APPEND));
  if ((fd < 0) && (errno == ENOENT))
    {
      fd = open("/mnt/spif/index.html", (O_CREAT | O_WRONLY), 0666);
    }
  /* Write HTTP data to local file */

  (void)write(fd, &((*buffer)[offset]), datend - offset);

  close(fd);
}

上記の例では、 /mnt/spif/index.html にダウンロードしたデータを保存します。
ファイルは追加書き込みモードで保存されるため、必要に応じて lte_http_get のコマンドを実行する前に
NuttShell から rm /mnt/spif/index.html のコマンドを実行しファイルを削除してください。
保存したファイルの内容は cat /mnt/spif/index.html のコマンドを実行すると確認できます。

8.1.4. 参考

LTE 機能の詳細は、開発ガイドを参照してください。

8.2. LTE TLS サンプルアプリケーション

8.2.1. 概要

本サンプルプログラムは、LTE通信機能を用いて、TLSプロトコルでサーバに接続し、HTTP POSTを行うサンプルです。

8.2.1.1. 動作環境
  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

  • microSD カード

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

本サンプルプログラムではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

8.2.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションを行います。

    引数に examples/lte_tls を指定してコンフィグレーションを実行します。

    tools/config.py examples/lte_tls
    

    サンプルアプリケーションのコンフィグレーションを変更します。

    tools/config.py -m
    
    1. APNのパラメータを設定します。(ご使用のSIMに合わせて設定してください。)

      Application Configuration -> Spresense SDK -> Examples -> TLS data communication over LTE network example
      - Access Point Name (CONFIG_EXAMPLES_LTE_TLS_APN_NAME)
      - IP type Selection
      - Authentication type Selection
      - Username used for authentication (CONFIG_EXAMPLES_LTE_TLS_APN_USERNAME)
      - Password used for authentication (CONFIG_EXAMPLES_LTE_TLS_PASSWD)
      tutorial lte tls apn
      コンフィグレーション名 説明

      Access Point Name

      アクセスポイント名

      IP type Selection

      APNプロトコル。IPv4IPv6IPv4/v6 から選択します。

      Authentication type Selection

      認証タイプ。 NonePAPCHAP から選択します。

      Username used for authentication

      ユーザ名。 認証タイプに None を選択した場合、設定は無視されます。

      Password used for authentication

      パスワード。 認証タイプに None を選択した場合、設定は無視されます。

    2. HTTPSの設定をします。

      モデムのTLSプロトコルを使用することが可能です。
      使用する場合は、 モデムのTLSプロトコルを使用する を参照してください。

      Application Configuration -> Spresense SDK -> Examples -> TLS data communication over LTE network example -> Directory for server certification files (CONFIG_EXAMPLES_LTE_TLS_CERTS_PATH)
      tutorial lte tls certs

      HTTPSで使用するサーバのルート証明書を格納するディレクトリを指定します。
      デフォルトの設定では/mnt/sd0/CERTSとなっています。

      ディレクトリのパスを /mnt/spif/CERTS にするとSPI-Flashに変更することができます。

  3. ビルドを実行します。 make コマンドを実行しビルドします。

    make
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

  4. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

8.2.3. 動作確認

シリアルターミナルを開いて、lte_tls コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から lte_tls コマンドを実行します。

    lte_tls コマンドの使い方を以下に示します。

    nsh> lte_tls <url>
    url

    インターネット上に置かれているファイルのURLを指定します。 https:// から始まる必要があります。urlを指定しない場合、 https://example.com/post となります。

    事前にサーバのルート証明書をコンフィグレーションで記述したディレクトリに格納する必要があります。 ルート証明書のダウンロード方法は HTTPSで使用するサーバアクセス用ルート証明書のダウンロード方法 をご参照ください。

本サンプルアプリケーションはHTTPのPOSTメソッドを使用して指定されたurlに任意のデータを送信します。

lte_tls コマンドを実行した例を以下に示します。正常にデータを送信できた場合は "HTTP status code = 200" が出力されます。

nsh> lte_tls https://httpbin.org/post
app_restart_cb called. reason:Modem restart by application.
app_radio_on_cb called. result: 0
app_activate_pdn_cb called. result: 0
pdn.session_id : 1
pdn.active     : 1
pdn.apn_type   : 0x202
pdn.ipaddr[0].addr : 10.212.60.255
app_localtime_report_cb called: localtime : "19/12/10 : 17:26:18"
set localtime completed: 2019/12/10,17:26:18
HTTP status code = 200
app_deactivate_pdn_cb called. result: 0
app_radio_off_cb called. result: 0
nsh>

本サンプルアプリケーションでは、 create_http_post() の関数でPOSTするデータを作成しています。

static int create_http_post(const char    *host,
                            const char    *path,
                            char          *buffer,
                            size_t        buffer_size)
{
  const char *post_data = "Spresense!";
  const char http_post_request[] = "POST %s HTTP/1.1\r\n"
                                   "HOST: %s\r\n"
                                   "Connection: close\r\n"
                                   "Content-Length: %d\r\n"
                                   "\r\n"
                                   "%s";

  return snprintf(buffer, buffer_size,
                  http_post_request,
                  path,
                  host,
                  strlen(post_data),
                  post_data);
}

8.2.4. HTTPSで使用するサーバアクセス用ルート証明書のダウンロード方法

8.2.4.1. 概要

HTTPSなどTLSによるセキュアな通信を使ったサーバへアクセスするためには、ルート証明書をダウンロードしてアプリケーションで指定した場所にコピーしておく必要があります。

SpresenseではTLSによるセキュア通信を行うために MbedTLS を使用しており、以下の証明書に対応しています。

  • DER encoded binary X.509 (DER)

    DER形式のバイナリ証明書ファイル。拡張子は .cer

  • Base 64 encoded X.509 (PEM)

    Base 64形式のASCIIテキスト証明書ファイル。拡張子は .pem / .cer

ここでは、サーバアクセス用ルート証明書のダウンロード方法について説明します。

8.2.4.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でセキュア通信を行うことができます。

8.2.5. 参考

LTE 機能の詳細は、開発ガイドを参照してください。

8.3. LTE MQTT サンプルアプリケーション

8.3.1. 概要

本サンプルプログラムは、LTE通信機能を用いて、MQTTブローカに接続し、指定したトピック名に対してパブリッシュまたはサブスクライブするサンプルです。

8.3.1.1. 動作環境
  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

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

本サンプルプログラムではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

8.3.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/lte_mqtt を指定してコンフィグレーションを実行します。

    tools/config.py examples/lte_mqtt
    

    サンプルアプリケーションのコンフィグレーションを変更します。

    tools/config.py -m
    
    1. APNのパラメータを設定します。(ご使用のSIMに合わせて設定してください。)

      Application Configuration -> Spresense SDK -> Examples -> MQTT using LTE example
      - Access Point Name (CONFIG_EXAMPLES_LTE_MQTT_APN_NAME)
      - IP type (CONFIG_EXAMPLES_LTE_MQTT_APN_IPTYPE)
      - Authentication type (CONFIG_EXAMPLES_LTE_MQTT_APN_AUTHTYPE)
      - Username used for authentication (CONFIG_EXAMPLES_LTE_MQTT_APN_USERNAME)
      - Password used for authentication (CONFIG_EXAMPLES_LTE_MQTT_APN_PASSWD)
      tutorial lte mqtt apn
      コンフィグレーション名 説明

      Access Point Name

      アクセスポイント名

      IP type

      APNプロトコル。0~2の値を設定します。0: IPv4, 1: IPv6 2: IPv4/v6 から選択します。

      Authentication type

      認証タイプ。0~2の値を設定します。 0: None 、1: PAP 、2: CHAP から選択します。

      Username used for authentication

      ユーザ名。 認証タイプに None を選択した場合、設定は無視されます。

      Password used for authentication

      パスワード。 認証タイプに None を選択した場合、設定は無視されます。

      ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

      make
      
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

8.3.3. 動作確認

シリアルターミナルを開いて、lte_mqtt コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から lte_mqtt コマンドを実行します。

    lte_mqtt コマンドの使い方を以下に示します。

    nsh> lte_mqtt topicname <options>
    topicname

    パブリッシュ又はサブスクライブするトピック名

    options
    オプション 説明

    --host

    MQTTブローカのホスト名。デフォルトは localhost です。

    --port

    接続先ポート番号。デフォルトは 1883 です。

    --qos

    QOS。0~2の値を指定します。デフォルトは 2 です。

    --delimiter

    区切り文字。 デフォルトは \n です。

    --clientid

    クライアントID。 デフォルトは stdout_subscriber です。

    --username

    ユーザ名。 デフォルトは 未設定 です。

    --password

    パスワード。 デフォルトは 未設定 です。

    --showtopics

    トピック名の表示有無。 on または off を指定します。デフォルトは off です。

    --cafile <file>

    ルートCA証明書ファイルを指定します。

    --cert <file>

    クライアント証明書ファイルを指定します。

    --key <file>

    クライアント秘密鍵ファイルを指定します。

    --publish <message>

    <message>文字列をパブリッシュします。このオプションが無いときは、サブスクライブを行います。

    Spresenseでは localhost をサポートしていません。必ず --host でホスト名を指定してください。

8.3.3.1. PC上からパブリッシュしたメッセージをSpresenseでサブスクライブする

MQTTブローカに接続し、指定したトピック名に対してサブスクライブする手順について説明します。
サブスクライブの実行に成功するとシリアルポートにサブスクライブしたメッセージを表示します。

当該トピック名に対してメッセージをパブリッシュするためには、別途操作が必要です。mosquitto-clients による操作例については後述します。

lte_mqtt コマンドを実行した例を以下に示します。正常にサブスクライブが完了した場合は "Subscribed 0" が出力されます。 サブスクライブ処理を終了する場合は、ターミナル上で Ctrl-C を押すことでアプリケーションが終了し NuttShell プロンプトに戻ります。

nsh> lte_mqtt /test --host test.mosquitto.org
app_restart_cb called. reason:Modem restart by application.
app_radio_on_cb called. result: 0
app_activate_pdn_cb called. result: 0
pdn.session_id : 1
pdn.active     : 1
pdn.apn_type   : 0x202
pdn.ipaddr[0].addr : 10.212.60.255
app_localtime_report_cb called: localtime : "19/12/11 : 11:36:39"
set localtime completed: 2019/12/11,11:36:39
Connecting to test.mosquitto.org 1883
Connected 0
Subscribing to /test
Subscribed 0

サブスクライブしているトピック名に対してパブリッシュします。
ここではUbuntu 16.04で mosquitto_pub コマンドを実行してパブリッシュする例を示します。

  • 以下のコマンドを実行し mosquitto-clients をインストールします。

sudo apt-get install mosquitto-clients
  • mosquitto_pub コマンドを実行してメッセージをパブリッシュします。

mosquitto_pub -t /test -m "Hello Spresense world" -h test.mosquitto.org
  • パブリッシュに成功するとシリアルポートに以下のメッセージが出力されます。

Hello Spresense world
8.3.3.2. SpresenseからパブリッシュしたメッセージをPC上でサブスクライブする

続いて、MQTTブローカに接続し、指定したトピック名に対してパブリッシュする方法を説明します。
一つのパブリッシュ処理を完了するとアプリケーションは終了します。

当該トピック名に対してメッセージをサブスクライブするためには、別途操作が必要です。mosquitto-clients による操作例については後述します。

lte_mqtt コマンドを実行した例を以下に示します。正常にパブリッシュが完了した場合は "Published 0" が出力されます。

nsh> lte_mqtt /test --host test.mosquitto.org --publish "Hello Spresense world"
app_restart_cb called. reason:Modem restart by application.
app_radio_on_cb called. result: 0
app_activate_pdn_cb called. result: 0
pdn.session_id : 1
pdn.active     : 1
pdn.apn_type   : 0x202
pdn.ipaddr[0].addr : 10.198.58.124
app_localtime_report_cb called: localtime : "22/03/30 : 22:10:55"
set localtime completed: 2022/03/30,22:10:55
Connecting to test.mosquitto.org 1883
Connected 0
Publishing to /test
Published 0
app_deactivate_pdn_cb called. result: 0
app_radio_off_cb called. result: 0

mosquitto-clients を用いてサブスクライブする方法を以下に示します。

  • PC上で mosquitto_sub コマンドを実行してメッセージをサブスクライブします。

mosquitto_sub -t /test -h test.mosquitto.org
  • サブスクライブに成功するとPC上に以下のメッセージが出力されます。

mosquitto_sub -t /test -h test.mosquitto.org
Hello Spresense world
8.3.3.3. SSL/TLS接続を行う場合

前述した例ではポートの1883を使用していましたが、ここでは SSL/TLS 接続を利用してポート番号8883を使用する方法について説明します。

lte_mqtt コマンドを実行する際に --cafile オプションによりルート証明書のファイルパスを指定することができます。

test.mosquitto.org を使用する場合は、mosquitto.org.crt ファイルをダウンロードしてください。

このファイルを予め Spresense からアクセスできる場所へ格納しておきます。 以下の例では、 Flash 上へ転送する方法を示しています。

./tools/flash.sh -c /dev/ttyUSB0 -w mosquitto.org.crt

転送に成功すると、Spresense からは /mnt/spif/mosquitto.org.crt のパスでこのファイルにアクセスすることができます。

lte_mqtt コマンドの実行方法は、--port--cafile オプションを追加することを除けば、先ほどと手順は同じです。

  • SSL/TLS 接続による MQTT パブリッシュの例

nsh> lte_mqtt /test --host test.mosquitto.org --port 8883 --cafile /mnt/spif/mosquitto.org.crt --publish testmessage
  • SSL/TLS 接続による MQTT サブスクライブの例

nsh> lte_mqtt /test --host test.mosquitto.org --port 8883 --cafile /mnt/spif/mosquitto.org.crt

8.3.4. 参考

LTE 機能の詳細は、開発ガイドを参照してください。

8.4. LTE LwM2M サンプルアプリケーション

8.4.1. 概要

本サンプルプログラムは、LTEによる通信機能と WakaamaのLwM2Mライブラリ を用いて、 Leshan などのLwM2Mサーバに接続し、Spresenseデバイスの管理を行うサンプルです。Spresense GNSS機能により取得されたデバイスの位置情報をサーバー経由で取得することができます。

8.4.2. 動作環境

  • デバイス

    • Spresense メインボード

    • Spresense LTE拡張ボード

    • SIM カード

  • サービス

8.4.3. 事前準備

8.4.3.1. デバイスの接続

SpresenseメインボードとSpresense LTE拡張ボードの接続方法、及びSIMカードの挿入方法はこちらを参照してください。 また、ネットワークに接続するためのSIMカードは LTE-M 動作確認SIM List を参照の上準備してください。

8.4.4. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションを行います。

    引数に examples/lte_lwm2m を指定してコンフィグレーションを実行します。

    tools/config.py examples/lte_lwm2m
    

    必要に応じてサンプルアプリケーションのコンフィグレーションを変更します。

    tools/config.py -m
    

    APNのパラメータを設定します。(ご使用のSIMに合わせて設定してください。)

    Application Configuration -> Spresense SDK -> Examples -> LwM2M using LTE example
    - Access Point Name (CONFIG_EXAMPLES_LTE_LWM2M_APN_NAME)
    - IP type (CONFIG_EXAMPLES_LTE_LWM2M_APN_IPTYPE)
    - Authentication type (CONFIG_EXAMPLES_LTE_LWM2M_APN_AUTHTYPE)
    - Username used for authentication (CONFIG_EXAMPLES_LTE_LWM2M_APN_USERNAME)
    - Password used for authentication (CONFIG_EXAMPLES_LTE_LWM2M_APN_PASSWD)
    tutorial lte lwm2m apn
    コンフィグレーション名    説明

    Access Point Name

    アクセスポイント名

    IP type

    APNプロトコル。0~2の値を設定します。 0: IPv4, 1: IPv6, 2: IPv4/v6 から選択します。

    Authentication type

    認証タイプ。0~2の値を設定します。 0: None, 1: PAP, 2: CHAP から選択します。

    Username used for authentication

    ユーザ名。 認証タイプに None を選択した場合、設定は無視されます。

    Password used for authentication

    パスワード。 認証タイプに None を選択した場合、設定は無視されます。

  3. ビルドを行います。

    make
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

  4. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

8.4.5. 動作確認

シリアルターミナルを開いて、lte_lwm2m コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から lte_lwm2m コマンドを実行します。

    lte_lwm2m コマンドの使い方を以下に示します。

    nsh> lte_lwm2m <options>
    options
    オプション    説明

    -n <NAME>

    LwM2M のエンドポイント名を指定します。

    -h <HOST>

    LwM2MサーバのURLを指定します。

    -p <PORT>

    LwM2Mサーバへの接続ポートを指定します。

    -4

    LwM2Mサーバへの接続をIPv4で行う場合オプションに追加します。(デフォルトはIPv6接続です。)

    -c

    このオプションを追加した場合、ダミーのバッテリーレベルを時間経過で上書き更新します。

    lte_lwm2m コマンドを実行した例を以下に示します。

8.4.5.1. Spresenseターミナル

任意のエンドポイント名(例: Spresense_LwM2M)を引数に指定して、lte_lwm2m コマンドを実行します。

nsh> lte_lwm2m -h leshan.eclipseprojects.io -p 5683 -4 -c -n Spresense_LwM2M
app_restart_cb called. reason:Modem restart by application.
app_radio_on_cb called. result: 0
app_activate_pdn_cb called. result: 0
pdn.session_id : 1
pdn.active     : 1
pdn.apn_type   : 0x202
pdn.ipaddr[0].addr : xxx.xxx.xxx.xxx
Trying to bind LWM2M Client to port 56830
LWM2M Client "Spresense_LwM2M" started on port 56830
> New Battery Level: 71
value changed!
app_localtime_report_cb called: localtime : "21/04/06 : 20:39:23"
set localtime completed: 2021/04/06,20:39:23
 -> State: STATE_REGISTERING
22 bytes received from [23.97.187.154]:5683
64 41 70 48  48 70 FB C6  82 72 64 0A  37 69 6D 77   dApHHp...rd.7imw
41 77 73 79  4C 36                                   AwsyL6
New Battery Level: 41
value changed!
 -> State: STATE_READY
 -> State: STATE_READY
8.4.5.2. Leshan LwM2M Server

Leshan LwM2M Server へアクセスして、Client Endpoint一覧から上で指定したエンドポイント名を選択します。

tutorial lte lwm2m leshan

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

本サンプルアプリケーションで実装されているオブジェクトについて以下に示します。

  • Device オブジェクト

    サーバーからのRead操作によってデバイス情報を読み出すことができます。

    表 1. Device Object
    リソース 説明

    Manufacturer

    "SONY SPRESENSE"

    Model Number

    "CXD5602PWBMAIN1"

    Serial Number

    ボード固有のシリアルナンバー (ボードユニークID)

    Firmware Version

    ファームウェアバージョン(アプリケーションのバージョン情報)

    Reboot

    EXEを実行するとボードを再起動します。

    Battery Level

    ランダムなダミー値です。

    Memory Free

    ヒープの空き容量(Kbyte)

    Current Time

    現在時刻

    UTC Offset

    "+09:00"

    Timezone

    "Asia/Japan"

    Memory Total

    アプリケーション用メモリのトータル容量(1.5MByte)

  • Firmware Update オブジェクト

    Package からパッケージファイルを送信し、Update によってファームウェアアップデートを実行することができます。

    表 2. Firmware Update Object
    リソース 説明

    Package

    File Inputからパッケージファイルを選択してデバイスへ送信します。

    Update

    EXEを実行するとファームウェアアップデートを実行します。

    Package から選択するファイルは、fwupdate/package.shを用いて作成します。 nuttx.spk を含むパッケージファイルを作成するときは次のように実行すると、package.bin ファイルが作成できます。

    cd spresense/sdk
    ../examples/fwupdate/package.sh nuttx.spk
    

    Package ボタンを押して、File Inputpackage.bin ファイルを選択して書き込みます。 ファイルのサイズにも依存しますが、書き込みには約15分ぐらいかかります。 このときLeshan LwM2M ServerのTimeout値が小さいと書き込みに失敗するので、 タイムアウトを30minに設定してから書き込みを実行してください。

    tutorial lte lwm2m leshan timeout

    書き込みが終了したら、Update を実行するとファームウェアアップデートを行います。

    ファームウェアアップデートを実行するとボードは再起動します。 再起動後にもLeshanサーバーへ自動的に接続したい場合、アプリケーションの自動起動方法 を参照してください。

  • Location オブジェクト

    Spresense GNSSから取得したデバイスの位置情報をサーバー経由で取得することができます。 非測位状態のとき、緯度、経度の値は 0.0、TimestampJan 01 1970 になります。 測位できたときにこれらの値が更新されます。

    表 3. Location Object
    リソース 説明

    Latitude

    緯度 (例) -43.5723

    Longitude

    経度 (例) 153.21760

    Altitude

    高度 [m]

    Radius

    精度 [m]

    Velocity

    速度情報 [3GPP-TS_23.032]

    Timestamp

    測位時刻

    Speed

    移動速度 [m/s]

  • Digital Input オブジェクト

    サーバーからのRead操作によってデジタルピンの入力値を読み出すことができます。 ピン番号は nuttx/arch/arm/include/cxd56xx/pin.h を参照してください。

    CREATEボタンを押して、Instance Id にピン番号、Application Type に任意の名前を入力してインスタンスを作成します。 ここの例では、LTE拡張ボードに付属のボタンスイッチ(ピン番号=89)を指定しています。これはプルアップされているピンなので、Digital Input Polarity に true を設定します。

    tutorial lte lwm2m leshan create
    tutorial lte lwm2m leshan instance

    インスタンスを作成した後にRead読み出しを行うことでGPIO値を読み出すことができます。 LTE拡張ボードに付属のボタンスイッチを押すと Digital Input State が true になり、離すと false になります。

    tutorial lte lwm2m leshan button
    表 4. Digital Input Object
    リソース 説明

    Digital Input State

    GPIOから読み出した値

    Digital Input Counter

    Edge Selection を使用した場合に割り込み回数を表示します。

    Digital Input Polarity

    極性

    Digital Input Edge Selection

    割り込みトリガの種類を選択します

    Digital Input Counter Reset

    割り込み回数をクリアします

    Application Type

    任意の名前

    Timestamp

    GPIOを読み出した時刻

  • Digital Output オブジェクト

    サーバーからのWrite操作によって任意のデジタルピンを制御することができます。 ピン番号は nuttx/arch/arm/include/cxd56xx/pin.h を参照してください。

    Digital Inputと同様に、 CREATEボタンを押して、Instance Id にピン番号、Application Type に任意の名前を入力してインスタンスを作成します。ここの例では、メインボードのLED0(ピン番号=97)を指定しています。

    tutorial lte lwm2m leshan led0

    Digital Output State に true/false を書き込むことで、LEDの点灯/消灯を制御することができます。

    tutorial lte lwm2m leshan led
    表 5. Digital Output Object
    リソース 説明

    Digital Output State

    GPIO出力設定値

    Digital Output Polarity

    極性

    Application Type

    任意の名前

    Timestamp

    GPIOを書き込んだ時刻

  • Analog Input オブジェクト

    サーバーからのRead操作によって任意のアナログピンの値を読み出すことができます。 LTE拡張ボードのSEN_AINアナログピン番号は、A/D変換器の使用方法 を参照してください。

    CREATEボタンを押して、Instance Id にSEN_AINアナログピン番号、Application Type に任意の名前を入力してインスタンスを作成します。ここの例では、LTE拡張ボード上のSEN_AIN0番号を指定しています。

    tutorial lte lwm2m leshan ain

    Analog Input Current Value に0.0~1.0の範囲で正規化されたアナログ値を読み出すことができます。

    tutorial lte lwm2m leshan analog
    表 6. Analog Input Object
    リソース 説明

    Analog Input Current Value

    ピンから読み出したアナログ値

    Min Measured Value

    読み出したアナログ値の最小値

    Max Measured Value

    読み出したアナログ値の最大値

    Min Range Value

    レンジ最小値(0.0)

    Max Range Value

    レンジ最大値(1.0)

    Application Type

    任意の名前

    Reset Min and Max Measured Values

    最小値/最大値をクリアする

    Timestamp

    Analogを読み出した時刻

8.4.6. 参考

LwM2M の詳細仕様については以下を参照してください。

LTE 機能の詳細は、開発ガイドを参照してください。

8.5. LTE AWS-IoT サンプルアプリケーション

8.5.1. 概要

本サンプルプログラムは、LTE通信機能とAWS-IoT device SDK for embedded Cを用いて、AWS-IoTサーバに接続し、MQTT通信を行うサンプルです。

8.5.2. 動作環境

  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

  • microSD カード

  • AWS-IoT Core

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

本サンプルプログラムではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

また、AWS-IoTを利用するには、AWS-IoTの使用開始および、AWS-IoTへのデバイス(モノ)の登録が必要です。

開始手順については AWS IoT の使用開始 を参考にしてください。

詳細はAWS-IoT Coreでの説明を参照していただくことになりますが、サンプルの動作確認に当たりAWS-IoT Coreで必要になる操作の要点は以下になります。

・ モノの作成とアタッチ
・ 証明書の発行
・ ポリシーの作成とアタッチ

  1. モノの作成とアタッチ
    モノは、特定のデバイスまたは論理エンティティを表します。
    証明書にモノをアタッチさせる必要があります。

  2. 証明書の発行
    モノやポリシーは使用する証明書にアタッチさせる必要があります。
    アタッチ方法については、 証明書へのアタッチ を参考にしてください。

    デバイスにて使用するため、発行した証明書をダウンロードします。ダウンロードするファイルは以下の3つです。

    ・ ルート証明書: AmazonRootCA1.pem
    ・ クライアント証明書: c3c4ff2375-certificate.pem.crt
    ・ 秘密鍵: c3c4ff2375-private.pem.key

    ビルド/参照時に、上記証明書は名前を変更して利用します。
    また、ダウンロードしたファイルは使用するmicroSD カードにCERTSディレクトリを生成して格納します。

  3. ポリシーの作成とアタッチ
    ポリシーにてデバイスがアクセスできるAWS IoT リソースが決定されます。
    証明書にポリシーをアタッチさせる必要があります。

ここでの証明書のファイル名は例を示すためのものです。環境によって異なるため適宜読み替えてください。

8.5.3. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/lte_awsiot device/sdcard を指定してコンフィグレーションを実行します。

    tools/config.py examples/lte_awsiot device/sdcard
    

    サンプルアプリケーションのコンフィグレーションを変更します。

    tools/config.py -m
    
    1. APNのパラメータを設定します。(ご使用のSIMに合わせて設定してください。)

      Application Configuration -> Spresense SDK -> Examples -> AWS IoT using LTE example
      - Access Point Name (CONFIG_EXAMPLES_LTE_AWSIOT_APN_NAME)
      - IP type (CONFIG_EXAMPLES_LTE_AWSIOT_APN_IPTYPE)
      - Authentication type (CONFIG_EXAMPLES_LTE_AWSIOT_APN_AUTHTYPE)
      - Username used for authentication (CONFIG_EXAMPLES_LTE_AWSIOT_APN_USERNAME)
      - Password used for authentication (CONFIG_EXAMPLES_LTE_AWSIOT_APN_PASSWD)
      tutorial lte awsiot apn
      コンフィグレーション名 説明

      Access Point Name

      アクセスポイント名

      IP type

      APNプロトコル。0~2の値を設定します。0: IPv4, 1: IPv6 2: IPv4/v6 から選択します。

      Authentication type

      認証タイプ。0~2の値を設定します。 0: None 、1: PAP 、2: CHAP から選択します。

      Username used for authentication

      ユーザ名。 認証タイプに None を選択した場合、設定は無視されます。

      Password used for authentication

      パスワード。 認証タイプに None を選択した場合、設定は無視されます。

    2. AWS-IoTを利用するためのパラメータを設定します。(ご使用の環境にあわせて設定してください。)

      Application Configuration -> Spresense SDK -> Examples -> AWS IoT using LTE example
      - AWS IoT cert folder (CONFIG_EXAMPLES_LTE_AWSIOT_CERT)
      コンフィグレーション名 デフォルト値 説明

      AWS IoT cert folder

      "/mnt/sd0/CERTS"

      AWS-IOTと接続する際に必要な認証用ファイルの置き場所を設定します。

      Application Configuration -> Spresense SDK -> Externals -> AWS IoT Device SDK for Embedded C -> AWS-IoT console
      - AWS_IOT_MQTT_HOST (CONFIG_EXTERNALS_AWSIOT_AWS_IOT_MQTT_HOST)
      - AWS_IOT_MQTT_PORT (CONFIG_EXTERNALS_AWSIOT_AWS_IOT_MQTT_PORT)
      - AWS_IOT_MQTT_CLIENT_ID (CONFIG_EXTERNALS_AWSIOT_AWS_IOT_MQTT_CLIENT_ID)
      - AWS_IOT_MY_THING_NAME (CONFIG_EXTERNALS_AWSIOT_AWS_IOT_MY_THING_NAME)
      - AWS_IOT_ROOT_CA_FILENAME (CONFIG_EXTERNALS_AWSIOT_AWS_IOT_ROOT_CA_FILENAME)
      - AWS_IOT_CERTIFICATE_FILENAME (CONFIG_EXTERNALS_AWSIOT_AWS_IOT_CERTIFICATE_FILENAME)
      - AWS_IOT_PRIVATE_KEY_FILENAME (CONFIG_EXTERNALS_AWSIOT_AWS_IOT_PRIVATE_KEY_FILENAME)
      tutorial lte awsiot console
      コンフィグレーション名 デフォルト値 説明

      AWS_IOT_MQTT_HOST

      MQTTブローカー名。ご使用のAWS-IoT環境のエンドポイントを設定します。

      AWS_IOT_MQTT_PORT

      443

      MQTTブローカーのポート番号。AWS-IoTはデフォルト値で接続可能です。

      AWS_IOT_MQTT_CLIENT_ID

      "AWS-IoT-C-SDK"

      MQTTのクライアントID。デバイス毎にユニークである必要があります。

      AWS_IOT_MY_THING_NAME

      "AWS-IoT-C-SDK"

      モノの名前。

      AWS_IOT_ROOT_CA_FILENAME

      "rootCA.crt"

      ルート証明書のファイル名。AWS-IoTサイトからダウンロードしたファイルを指定します。

      AWS_IOT_CERTIFICATE_FILENAME

      "cert.pem"

      クライアント証明書のファイル名。AWS-IoTサイトからダウンロードしたファイルを指定します。

      AWS_IOT_PRIVATE_KEY_FILENAME

      "privkey.pem"

      クライアント認証で用いる秘密鍵のファイル名。AWS-IoTサイトからダウンロードしたファイルを指定します。

      ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

      make
      
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

AWS-IoT Coreコードのデバッグ情報を出力したい場合は、menuconfigから 'CONFIG_EXTERNALS_AWSIOT_ENABLE_IOT_DEBUG’を有効にしてビルドを行ってください。

TRACE, INFO, WARN, ERRORの情報についても、同様な設定を行うことで出力が可能になります。
'CONFIG_EXTERNALS_AWSIOT_ENABLE_IOT_TRACE',
'CONFIG_EXTERNALS_AWSIOT_ENABLE_IOT_INFO',
'CONFIG_EXTERNALS_AWSIOT_ENABLE_IOT_WARN',
'CONFIG_EXTERNALS_AWSIOT_ENABLE_IOT_ERROR'

その際、併せて以下のSTACKSIZEも増やす必要があります。
'CONFIG_EXAMPLES_LTE_AWSIOT_STACKSIZE',
'CONFIG_EXAMPLES_LTE_AWSIOT_STACKSIZE_IN_USING_MBEDTLS'

8.5.4. 動作確認

シリアルターミナルを開いて、lte_awsiot コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から lte_awsiot コマンドを実行します。

    lte_awsiot コマンドの使い方を以下に示します。

    nsh> lte_awsiot <options>
    options
    オプション 説明

    -x

    パブリッシュの繰り返し回数。デフォルト値は無限回です。

    本サンプルアプリケーションはAWS-IoTにMQTTで接続し、"sdkTest/sub"トピックにサブクライブします。
    サブスクライブ完了後、"sdkTest/sub"トピックにパブリッシュします。

lte_awsiot コマンドを実行した例を以下に示します。

nsh> lte_awsiot -x 1
app_restart_cb called. reason:Modem restart by application.
app_radio_on_cb called. result: 0
app_activate_pdn_cb called. result: 0
pdn.session_id : 1
pdn.active     : 1
pdn.apn_type   : 0x202
pdn.ipaddr[0].addr : 10.212.60.255
app_localtime_report_cb called: localtime : "19/12/13 : 14:15:16"
set localtime completed: 2019/12/13,14:15:16
app_deactivate_pdn_cb called. result: 0
app_radio_off_cb called. result: 0

送信したパブリッシュメッセージはAWS-IoTコンソール上で確認できます。

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

8.5.5. 参考

LTE 機能の詳細は、開発ガイドを参照してください。

8.6. LTE Azure-IoT サンプルアプリケーション

8.6.1. 概要

本サンプルプログラムは、LTEによる通信機能とNuttXのWebClientを用いて、Azure IoT Hubに接続し、メッセージの送受信及びファイルの送受信を行うサンプルです。

8.6.2. 動作環境

  • デバイス

    • Spresense メインボード

    • Spresense LTE拡張ボード

    • SIM カード

    • microSD カード

  • サービス

    • Azure IoT Hub

8.6.3. 事前準備

8.6.3.1. デバイスの接続

SpresenseメインボードとSpresense LTE拡張ボードの接続方法、及びSIMカードの挿入方法はこちらを参照してください。 また、ネットワークに接続するためのSIMカードは LTE-M 動作確認SIM List を参照の上準備してください。

8.6.3.2. Azure IoT Hub/IoT デバイスの作成

Azure IoT Hubを利用するには、 Azure Portal からAzure IoT Hub及びIoTデバイスを作成しておく必要があります。

Azure IoT HubとIoT デバイスについては Azure Portal を使用して IoT Hub を作成する を参照し作成してください。

8.6.3.3. 接続用ファイルの作成

Azure IoT Hubに接続しSpresenseとデータの送受信を行うには、 Azure IoT サーバ証明書 と Azure IoT 設定ファイル が必要になります。 以下にそれぞれの取得方法及びファイルの保存方法について説明します。

Azure IoT サーバ証明書

Azure IoT Hubサーバへ接続するためには、Azure IoT サーバ証明書をダウンロードし、SDカードに保存しておく必要があります。

サイトAzure Portal https://portal.azure.com/Baltimore CyberTrust Root証明書 (ファイル名 portal-azure-com.pem) を、 SDカードの CERTS ディレクトリの中にコピーしてください。

SDカード
└── CERTS
    └── portal-azure-com.pem

例としてFirefox webブラウザを利用したダウンロード方法を説明します。

Firefox以外のブラウザを使ったルート証明書のダウンロード方法は HTTPSで使用するサーバアクセス用ルート証明書のダウンロード方法 をご参照ください。

  1. Azure Portal https://portal.azure.com にアクセスします。

  2. Firefoxブラウザのアドレスバー左横の認証ボタンをクリックします。

    tutorial lte azureiot browser auth
  3. 安全な接続 から 詳細を表示 をクリックします。

    tutorial lte azureiot browser safe connection ja
    tutorial lte azureiot browser detail ja
  4. セキュリティ から 証明書を表示 をクリックします。

    tutorial lte azureiot browser safe security ja
  5. Baltimore CyberTrust Root から PEM (証明書) をクリックし portal-azure-com.pem をダウンロードします。

    tutorial lte azureiot browser safe cybertrust ja
  6. SDカード直下に CERTS ディレクトリを作成し、その中にダウンロードした portal-azure-com.pem をコピーします。

  7. コピーしたSDカードをSpresense LTE拡張ボードに挿入します。

Azure IoT 設定ファイル

lte_azureiotサンプルでAzure IoT Hubへ接続するためには、IoT Hub及びIoT デバイス名、IoT デバイスのプライマリ対象共有アクセスキー情報を含んだファイルをSDカードにコピーする必要があります。

Azure Portalで作成したAzure IoT Hub名 <IoT Hub Name> 及びIoT デバイス名 <Device ID> と、プライマリ対象共有アクセスキー <Primary Key> を以下のフォーマット通りに記載し resources.txt として保存します。(プライマリ対象共有アクセスキーは、作成したIoT デバイス情報の中の 主キー になります。)

<IoT Hub Name>
<Device ID>
<Primary Key>

そして、SDカードに azureiot を作成しその中に resources.txt をコピーします。

SDカード
└── azureiot
    └── resources.txt

8.6.4. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションを行います。

    引数に examples/lte_azureiot を指定してコンフィグレーションを実行します。

    tools/config.py examples/lte_azureiot
    

    必要に応じてサンプルアプリケーションのコンフィグレーションを変更します。

    tools/config.py -m
    

    APNのパラメータを設定します。(ご使用のSIMに合わせて設定してください。)

    Application Configuration -> Spresense SDK -> Examples -> Azure IoT using LTE example
    - Access Point Name (CONFIG_EXAMPLES_LTE_AZUREIOT_APN_NAME)
    - IP type Selection (CONFIG_EXAMPLES_LTE_AZUREIOT_APN_IPTYPE_*)
    - Authentication type Selection (CONFIG_EXAMPLES_LTE_AZUREIOT_APN_AUTHTYPE_*)
    - Username used for authentication (CONFIG_EXAMPLES_LTE_AZUREIOT_APN_USERNAME)
    - Password used for authentication (CONFIG_EXAMPLES_LTE_AZUREIOT_APN_PASSWD)
    tutorial lte azureiot apn
    コンフィグレーション名    説明

    Access Point Name

    アクセスポイント名

    IP type Selection

    APNプロトコル。0~2の値を設定します。 IPv4, IPv6, IPv4/v6 から選択します。

    Authentication type Selection

    認証タイプ。0~2の値を設定します。 None, PAP, CHAP から選択します。

    Username used for authentication

    ユーザ名。 認証タイプに None を選択した場合、設定は無視されます。

    Password used for authentication

    パスワード。 認証タイプに None を選択した場合、設定は無視されます。

  3. ビルドを行います。

    make
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

  4. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

8.6.5. 動作確認

シリアルターミナルを開いて、lte_azureiot コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から lte_azureiot コマンドを実行します。

    lte_azureiot コマンドの使い方を以下に示します。

    nsh> lte_azureiot <options> <arg1> <arg2>
    options
    オプション    説明

    send

    Azure IoT Hubに対してメッセージを送信します。メッセージ内容は <arg0>"Hello!" などのようにコマンドを実行します。

    recv

    Azure IoTからのメッセージを受信します。

    upload

    Azure IoT Hubのコンテナにファイルをアップロードします。アップロードするファイルは <arg1>, コンテナ上でのファイル名を <arg2> として指定します。

    download

    Azure IoT Hubのコンテナからファイルをダウンロードします。ダウンロードするファイル名は <arg1>, ダウンロードしたファイルの置き場所を <arg2> として指定します。

    lte_azureiot コマンドを実行した例を以下に示します。

8.6.5.1. メッセージを送信する場合

Azure Cloud Shell上で az iot hub monitor-events コマンドを実行してモニターします。

Spresenseターミナル
nsh> lte_azureiot send "spresense hello!"
LTE connect...
LTE connect...OK

Device message: spresense hello! --> Cloud
Successful

LTE disconnect...
lte_radio_off
lte_power_off
lte_finalize
LTE disconnect...OK
Azure Cloud Shell
azure_user@Azure:~$ az iot hub monitor-events --hub-name <IoT Hub Name>
Starting event monitor, use ctrl-c to stop...
{
    "event": {
        "origin": "<Device ID>",
        "module": "",
        "interface": "",
        "component": "",
        "payload": "spresense hello!"
    }
}
8.6.5.2. メッセージを受信する場合

Azure Cloud Shell上で az iot device c2d-message send コマンドを実行してメッセージを送信します。

Azure Cloud Shell
azure_user@Azure:~ az iot device c2d-message send -d <Device ID> --data "spresense hello azure" -n <IoT Hub Name>
Spresenseターミナル
nsh> lte_azureiot recv
LTE connect...
LTE connect...OK

Successful
Recv message: spresense hello azure

LTE disconnect...
lte_radio_off
lte_power_off
lte_finalize
LTE disconnect...OK

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

8.6.6. 参考

LTE 機能の詳細は、開発ガイドを参照してください。

8.7. LTE Websocket サンプルアプリケーション(websocket_gmocoin)

8.7.1. 概要

本サンプルプログラムは、LTE通信機能及びWebsocketライブラリを用いて、GMOコイン社で提供している仮想通貨取引情報の取得を行うものです。 GMOコイン社のAPIについては、 GMOコイン Public WebSocket API(外部リンク) をご参照ください。

8.7.1.1. 動作環境
  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード

  • microSD カード

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

本サンプルプログラムではネットワークに接続するため、SIM カードが必要です。
LTE-M 動作確認SIM Listをご確認ください。

8.7.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションを行います。

    引数に examples/lte_websocket_gmocoin を指定してコンフィグレーションを実行します。

    tools/config.py examples/lte_websocket_gmocoin
    
  3. ビルドを実行します。 make コマンドを実行しビルドします。

    make
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

  4. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

8.7.3. TLS証明書のコピー

本サンプルプログラムを実行するためには、GMOコイン社のAPI用サーバルート証明書が必要です。 以下の手順に従い、証明書をダウンロードし所定の場所にコピーしてください。

  1. https://api.coin.z.com/ にアクセスしルート証明書をダウンロードします。

  2. ダウンロードしたルート証明書を rootcacert_r3.cer にリネームし、SDカードのトップディレクトリにコピーします。

  3. コピー先のSDカードをLTE拡張ボードに挿入します。

8.7.4. 動作確認

シリアルターミナルを開いて、LTEネットワークへの接続を行い websocket_gmocoin コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から lte_sysctl コマンドを実行しLTEデーモンを開始します。

    以下の例は、APN名が internet 、認証方式が CHAP 、ユーザ名が user 、そしてパスワードが pass の場合の例です。

    nsh> lte_sysctl -a internet -v 2 -u user -p pass start
  3. NuttShell から ifup コマンドを実行しLTEネットワークへの接続を行います。

    nsh> ifup eth0
    ifup eth0...OK
  4. NuttShell から websocket_gmocoin コマンドを実行します。

    websocket_gmocoin コマンドの使い方を以下に示します。

    nsh> websocket_gmocoin <command> <symbol>
    command

    サブスクライブするか解除するかを選択します。

    • subscribe: サブスクライブする

    • unsubscribe: サブスクライブを解除する

    symbol

    サブスクライブ(あるいは解除)する取引情報の種類を選択します。 使用できる symbol は以下のとおりです。

        BTC     : Bitcoin
        ETH     : Ethereum
        BCH     : Bitcoin Cash
        LTC     : Litecoin
        XRP     : Ripple
        XEM     : New Economy Movement
        XLM     : Stellar
        BTC_JPY : Bitcoin - Yen
        ETH_JPY : Ethereum - Yen
        BCH_JPY : Bitcoin Cash - Yen
        LTC_JPY : Litecoin - Yen
        XRP_JPY : Ripple - Yen

websocket_gmocoin コマンドを実行した例を以下に示します。正常にコマンドを実行できた場合は、取引情報の更新があるタイミングで取引情報がJsonフォーマットで出力されます。

nsh> websocket_gmocoin subscribe BTC
nsh> wss main task started.
{"channel":"ticker","ask":"5365700","bid":"5365300","high":"5452200","last":"5365500","low":"5288150","symbol":"BTC","timestamp":"2022-04-07T05:20:17.468Z","volume":"257.2177"}
{"channel":"ticker","ask":"5365950","bid":"5365300","high":"5452200","last":"5365900","low":"5288150","symbol":"BTC","timestamp":"2022-04-07T05:21:39.003Z","volume":"257.1498"}
{"channel":"ticker","ask":"5365950","bid":"5365300","high":"5452200","last":"5365950","low":"5288150","symbol":"BTC","timestamp":"2022-04-07T05:21:39.003Z","volume":"257.1498"}
{"channel":"ticker","ask":"5366150","bid":"5365300","high":"5452200","last":"5366150","low":"5288150","symbol":"BTC","timestamp":"2022-04-07T05:21:59.119Z","volume":"257.2198"}
{"channel":"ticker","ask":"5366200","bid":"5365300","high":"5452200","last":"5366200","low":"5288150","symbol":"BTC","timestamp":"2022-04-07T05:22:24.583Z","volume":"257.2297"}

8.7.5. 参考

LTE 機能の詳細は、開発ガイドを参照してください。

8.8. SMS サンプルアプリケーション

8.8.1. 概要

本サンプルプログラムは、SMS(Short Message Service) の送信及び受信を行うサンプルです。

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

8.8.1.1. 動作環境
  • Spresense メインボード

  • Spresense LTE拡張ボード

  • SIM カード (SMS機能付き)

接続方法は、SpresenseメインボードとSpresense LTE拡張ボードの接続方法を参照してください。 また、SMSを利用するためにはSMS対応のSIMカードを用意する必要があります。 LTE-M 動作確認SIM List を参照の上SMSに対応したプランのSIMカードをご用意ください。

8.8.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、 config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/sms_sendexamples/sms_recv を指定してコンフィグレーションを実行します。

    tools/config.py examples/sms_send examples/sms_recv
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

8.8.3. 動作確認

シリアルターミナルを開いて、 sms_recv コマンド、 sms_send コマンドを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から lte_sysctl コマンドと ifup コマンドを実行してネットワークに接続します。

    lte_sysctl コマンドと ifup コマンドの詳細は こちら を参照してください。

    nsh> lte_sysctl <options> start
    nsh>
    nsh> ifup eth0
    ifup eth0...OK
  3. NuttShell から sms_recv コマンドを実行してSMSの受信待ちをします。

    nsh> sms_recv &

    コマンドの最後に & をつけることで、コマンドをバックグラウンドで実行できます。

    sms_recv コマンドはSMSの受信待ちを行い、SMSを受信した際にNuttShellに受信メッセージを出力します。

  4. NuttShell から sms_send コマンドを実行してSMSを送信します。

    sms_send コマンドの使い方を以下に示します。

    nsh> sms_send <phone number> <text message> [<enable status report>]
    <phone number>

    宛先電話番号

    <text message>

    メッセージ本文

    <enable status report>

    SMSの受け取り確認通知を受信するかどうかのフラグ。 このパラメータは省略可能です。

    説明

    0

    SMSの受け取り確認通知を受信しません

    1

    SMSの受け取り確認通知を受信します

sms_send コマンドと sms_recv コマンドを実行した例を以下に示します。

nsh> sms_recv &
sms_recv [14:100]
nsh> socket open success:3
nsh>
nsh> sms_send 080xxxxxxxx こんにちは 1
socket open success:3
Successfully sent SMS to 080xxxxxxxx
Get reference id[0] = 241

SMSを端末が受信した際はNuttShellに以下のように出力されます

nsh> -----------------------------------------------
sent time            : 21/12/17 : 13:48:52 +09
message type         : Deliver message
source address length: 22
message body length  : 10
source address       : 080xxxxxxxx
message body         : こんにちは

sms_send コマンドで <enable status report> パラメータに 1 を入力して実行した場合、送信したSMSが宛先に到達した際に以下のようなメッセージがNuttShellに出力されます。

 sent time            : 21/12/17 : 13:48:52 +09
 message type         : Status report message
 source address length: 0
 message body length  : 10
 reference ID         : 241
 status               : success
 discharge time       : 21/12/17 : 13:48:57 +09

SMS受信時に表示される各パラメータについて説明します。

パラメータ名 説明

sent time

SMSがプロバイダのサーバに送信された時刻を表します。

message type

受信したメッセージの種別を表します。 Deliver message が 一般的なショートメッセージ、 Status report message がSMSの受け取り確認通知です。

source address length

UCS2文字セットでエンコードされた送信元電話番号のbyte数を表します。

message body length

UCS2文字セットでエンコードされた本文のbyte数を表します。

source address

送信元電話番号です。

message body

受信したSMSの本文です。

reference ID

送信したSMSの、どの受け取り確認通知かどうかを参照するIDです。 sms_send コマンドでSMSの送信が完了した後に出力される Get reference id[0] = xxx の値とこの値を参照することで、どの送信SMSの受け取り確認通知かどうかを判断できます。

status    

送信したSMSの到達結果を表します。 宛先にSMSが到達した場合は、 success が表示されます。 宛先にSMSが到達できなかった場合は、 failed または pending と表示されます。

discharge time

送信したSMSが宛先に到達した時刻を表します。

8.8.4. 参考

LTE 機能の詳細は、開発ガイドを参照してください。

8.9. LTEのネットワークデーモンを使ってネットワーク接続を行う

LTEのネットワークデーモンを使ってネットワーク接続するコマンド(lte_sysctl)について説明します。
ネットワーク接続に成功するとwgetなどのコマンドを使用してデータ通信が可能となります。

Spresense SDK v2.3.0 からコマンド名が lte_daemon から lte_sysctl に変わりました。v2.2.0以前のSDKをお使いの方は下記説明中の lte_sysctllte_daemon とお読み替えください。

8.9.1. lte_sysctl コマンドについて

lte_sysctl コマンドは、NuttShellコマンドライン上でLTEの電源・通信制御を行うコマンドです。

nsh> lte_sysctl <options> <action>

このコマンドを使うことで wgetnslookup などの通信系コマンドでネットワーク通信が可能になります。

このコマンドには action というサブコマンドがあり、それぞれの機能は以下の通りになっています。

サブコマンド (<action>)
action 説明

start

LTEのネットワークデーモンを開始します。

stop

LTEのネットワークデーモンを停止します。

stat

設定済みのアクセスポイント情報及び通信方式、LTEモジュールのファームウェアバージョンを表示します。

factoryreset

LTEモジュールの設定値を工場出荷時の値に戻します。 (※追加コンフィグレーションが必要)

また、 start サブコマンドには以下のようなオプションがあります。

start サブコマンドオプション (<options>)
オプション 説明

-a

アクセスポイント名(APN)。

-t

APNタイプ。16進数の値を設定します。詳細な値については lte_apn_setting.ip_type を参照してください。
指定がなければ IADEFAULT の論理和が使用されます。

-i

APNプロトコル。0~2の値を設定します。(0: IPv4 , 1: IPv6 , 2: IPv4/v6 )
指定がなければデフォルトは0: IPv4 です。

-v

認証タイプ。0~2の値を設定します。 (0: None , 1: PAP , 2: CHAP )
指定がなければデフォルトは0: None です。

-u

APNのユーザ名。 認証タイプに 0: None を選択した場合、設定は無視されます。

-p

APNのパスワード。 認証タイプに 0: None を選択した場合、設定は無視されます。

-r

LTEの通信方式。Cat.M1を使う場合は M1 、NB-IoTを使う場合は NB を選択します。指定しない場合は過去に設定した通信方式が選択されます。

8.9.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/lte を指定してコンフィグレーションを実行します。

    tools/config.py feature/lte
    

    APN情報を予め設定するコンフィグレーション方法は APN設定をデフォルト設定から変更する手順について を参照してコンフィグレーションを更新してください。

    factoryreset サブコマンドを有効にするためには factoryresetサブコマンドを有効にする手順について を参照してコンフィグレーションを更新してください。

  3. make を実行してビルドします。

    make
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

  4. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

    LTEを使用したexample(examples/lte_http_getなど)のコンフィグレーションでも
    CONFIG_LTE_SYSCTL が有効になっているためそのままお使い頂けます。

8.9.3. 動作確認

8.9.3.1. start サブコマンドを使ってLTE接続を開始する場合
  1. NuttShell から lte_sysctlstart サブコマンドで実行しLTEのデーモンを開始ます。

    以下のコマンドは APN名が internet 、認証方式が CHAP 、ユーザー名が user 、パスワードが pass の例になります。

    nsh> lte_sysctl -a internet -v 2 -u user -p pass start
  2. NuttShell から ifup eth0 コマンドを実行しLTEのネットワークインタフェースを有効にします。

    以下のように ifconfig コマンドを実行することによってネットワークアドレスが割り当てられていることがわかります。

    nsh> ifup eth0
    ifup eth0...OK
    nsh>
    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 00:00:00:00:00:00 at UP
            inet addr:100.92.28.11 DRaddr:0.0.0.0 Mask:0.0.0.0
            inet6 addr: ::/0
            inet6 DRaddr: ::/0
  3. ネットワークに接続した状態で、NuttShell から wget コマンドを実行してインターネット上に置かれているファイルのURLを指定しファイルをダウンロードします。

    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 00:00:00:00:00:00 at UP
            inet addr:100.92.28.11 DRaddr:0.0.0.0 Mask:0.0.0.0
            inet6 addr: ::/0
            inet6 DRaddr: ::/0
    
    nsh>
    nsh> wget -o /mnt/spif/index.html http://www.example.com/index.html
    nsh>
    nsh>
    nsh> cat /mnt/spif/index.html
    <!doctype html>
    <html>
    <head>
        <title>Example Domain</title>
    
        <meta charset="utf-8" />
        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <style type="text/css">
        body {
            background-color: #f0f0f2;
            margin: 0;
            padding: 0;
            font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
    
        }
        div {
            width: 600px;
            margin: 5em auto;
            padding: 2em;
            background-color: #fdfdff;
            border-radius: 0.5em;
            box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
        }
        a:link, a:visited {
            color: #38488f;
            text-decoration: none;
        }
        @media (max-width: 700px) {
            div {
                margin: 0 auto;
                width: auto;
            }
        }
        </style>
    </head>
    
    <body>
    <div>
        <h1>Example Domain</h1>
        <p>This domain is for use in illustrative examples in documents. You may use this
        domain in literature without prior coordination or asking for permission.</p>
        <p><a href="https://www.iana.org/domains/example">More information...</a></p>
    </div>
    </body>
    </html>
    nsh>
    
    nsh>
8.9.3.2. stat サブコマンドを使って接続状況を確認する場合
  1. NuttShell から lte_sysctlstat サブコマンドで実行しLTEの接続状況を確認します。

    nsh> lte_sysctl stat
    Daemon state : running
    APN
      Name: internet
      IP type: IPv4
      Authentication: CHAP
      Username: user
      Password: pass
    RAT: CAT-M1
    VER: RK_02_01_02_10_108_54

    上記の例ではデーモンの状態が running (動作中)で、APN名が internet 、APNのIPタイプが IPv4 、認証タイプが CHAP 、ユーザ名が user 、パスワードが pass 、通信方式が CAT-M1 、そしてLTEモジュールのファームウェアが RK_02_01_02_10_108_54 であることを示しています。

8.9.3.3. stop サブコマンドを使ってLTE接続を終了する場合
  1. NuttShell から ifdown eth0 コマンドを実行しLTEのネットワークインタフェースを無効にします。

    以下のように ifconfig コマンドを実行することによってネットワークアドレスの割り当てが解除されていることがわかります。

    nsh> ifdown eth0
    ifdown eth0...OK
    nsh>
    nsh> ifconfig
    eth0	Link encap:Ethernet HWaddr 00:00:00:00:00:00 at DOWN
    	inet addr:0.0.0.0 DRaddr:0.0.0.0 Mask:0.0.0.0
    	inet6 addr: ::/0
    	inet6 DRaddr: ::/0
  2. NuttShell から lte_sysctlstop サブコマンドで実行しLTEのデーモンを終了します。

    nsh> lte_sysctl stop
    nsh>
8.9.3.4. factoryreset サブコマンドを使ってLTEモジュールの設定値を工場出荷時の状態に戻す場合
  1. NuttShell から lte_sysctl factoryreset コマンドを実行します。

    nsh> lte_sysctl factoryreset
    Factory reset running...
    Please do not turn off the device. Factory reset takes around 30 sec.
    Factory reset done.

    factoryreset サブコマンドを有効にするためには factoryresetサブコマンドを有効にする手順について を参照してコンフィグレーションを更新してください。

    このコマンドの実行には30秒程度時間を要します。実行中はSpresenseの電源を切らないようにご注意ください。

    このコマンドはLTEモジュールのファームウェアのバージョンが RK_02_01_ から始まるボードでのみ使えます。
    それ以外のボードではお使いいただけません。

8.9.4. APN設定をデフォルト設定から変更する手順について

ここではlte_sysctlの引数に指定するAPN設定のデフォルト値を変更する手順を示します。

  1. lte_sysctlのコンフィグレーションを変更します。

    tools/config.py -m
    
  2. APNのパラメータを設定します。(ご使用のSIMに合わせて設定してください。)

    Application Configuration -> Spresense SDK -> System tools -> lte_sysctl system command
    - Access Point Name (CONFIG_LTE_SYSCTL_APN_NAME)
    - IP type Selection
    - Authentication type Selection
    - Username used for authentication (CONFIG_LTE_SYSCTL_APN_USERNAME)
    - Password used for authentication (CONFIG_LTE_SYSCTL_APN_PASSWD)
    tutorial system lte daemon apn
    コンフィグレーション名 デフォルト値 説明

    Access Point Name

    アクセスポイント名

    IP type Selection

    0: IPv4

    APNプロトコル。0~2の値を設定します。0: IPv4, 1: IPv6 2: IPv4/v6 から選択します。

    Authentication type Selection

    0: None

    認証タイプ。0~2の値を設定します。 0: None 、1: PAP 、2: CHAP から選択します。

    Username used for authentication

    ユーザ名。 認証タイプに None を選択した場合、設定は無視されます。

    Password used for authentication

    パスワード。 認証タイプに None を選択した場合、設定は無視されます。

8.9.5. factoryreset サブコマンドを有効にする手順について

ここではlte_sysctlで factoryreset サブコマンドを有効にする手順を示します。

  1. lte_sysctlのコンフィグレーションを変更します。

    tools/config.py -m
    
  2. Enable factoryreset sub-command にチェックを入れます。

    Application Configuration -> Spresense SDK -> System tools -> lte_sysctl system command
    [*]   Enable factoryreset sub-command
    tutorial system lte sysctl factoryreset

9. Digital Filter チュートリアル

9.1. digital_filter サンプルアプリケーション

この章では、digital_filterに関するサンプルアプリケーションの動作手順を示します。 このサンプルアプリケーションには、3つの NuttShell コマンドが含まれています。

NuttShell command name Overview

fir

入力されたWAVファイルに対して、各種(ローパス、ハイパス、帯域通過、帯域阻止)フィルタでフィルタ処理を行い、結果を指定のwavファイルに書き出します。

decimation

入力されたWAVファイルに対して、デシメーション(データの間引き)を行い、結果を指定のwavファイルに書き出します。

envelope

機械部品の劣化によるインパルス振動を検出するような場合によく用いられる、H関数を実装したサンプルコマンドになります。
入力のWAVファイルに対して、H関数処理をした結果を指定されたwavファイルに書き出します。
H関数に関しては以下のサイトが参考になるかと思います。
https://www.imv.co.jp/products/vibrograph/watch/info/h_kansu.php

9.1.1. ソースコード

このサンプルのソースコードは、 spresense/examples/digital_filter ディレクトリ内に入っています。

上記ディレクトリ内のにあるファイルは以下の通りです。

spresense/examples/digital_filter/

.
├── Kconfig
├── Make.defs
├── Makefile
├── fir_decimator_main.c
├── fir_envelope_main.c
├── fir_filter_main.c
├── wav.c
└── wav.h

ソースコードファイルの概要は以下の通りです。
このサンプルでは、一つのフォルダ内で、3つのNuttShellコマンドがビルドされるようになっています。

File name Overview

fir_filter_main.c

fir コマンドサンプルソースコード

fir_decimator_main.c

decimation コマンドサンプルソースコード

fir_envelope_main.c

envelope コマンドサンプルソースコード

wav.c

WAVファイルの読み書きするためのユーティリティソースコード

wav.h

WAVファイ読み書きするためのユーティリティコードのヘッダファイル

9.1.2. ビルドおよび実行手順

9.1.2.1. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/digital_filter を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/digital_filter
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 nuttx.spk
    

9.1.3. 動作確認

9.1.3.1. 事前準備

まずフィルタ処理を行う対象のWAVファイルをSpresense 拡張ボートのSDカードにコピーします。
PCにSDカードスロットがある場合、SDカードをPCに入れて、対象のWAVファイルをSDカードにコピーすることができますが、ここでは、カードスロットがないPCをお持ちの方向けに、Spresenseの拡張ボード側のmicroUSBを使ってUSBマスストレージとして認識させ、PCにファイルを保存する方法を記載します。

まず、Spresense拡張ボードのmicroSDカードスロットに、microSDカードを挿入し、Spresense拡張ボードのmicroUSBコネクタをPCと接続します。

SpresenseメインボードのmicroUSBポートとSpresense拡張ボードのmicroUSBポート両方をPCに接続してください。
どちらもデータ通信が行えるUSBケーブルである必要があります。
充電のみに対応したUSBケーブルでは動作しませんのでご注意ください。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    

    ターミナルでSpresenseボードに入ると、以下のようにNuttShellのプロンプトが表示されます。

    nsh>
  2. USBマスストレージでPCにマウントします。

    NuttShell上で、 msconn と入力し、USBマスストレージを有効にします。

    nsh> msconn

    これで、Spresense 拡張ボードに挿入したSDカードが、PC上にストレージデバイスとして接続されます。

  3. フィルタ処理を行う入力WAVファイルをSDカードにコピーします。

    このストレージデバイスの中に、フィルタ処理を行う入力信号となる、WAVファイルをコピーします。(仮にそのファイル名を input.wav とします)

  4. USBマスストレージをアンマウントします。

    コピーが終わったら、NuttShellプロンプト上で、 msdis を実行して、PCのストレージデバイス接続を終了しておきます。

    nsh> msdis

    マスストレージをアンマウントしておかないと、サンプルのコマンドを実行した際、結果が正しく書き出されない場合があります。

これで事前準備が完了しました。
あとは各コマンドを使って実際にフィルタ処理を行ます。

9.1.3.2. fir コマンドの動作確認

firコマンドは、入力WAVファイルに対して、FIRを用いた(ローパス、ハイパス、帯域通過、帯域停止)フィルタをかけ、結果を指定されたWAVファイルに書き出すコマンドになっています。

firコマンドのコマンド仕様は以下のようになります。

  • nsh> fir <input wav file> <lpf/hpf> <transition width(Hz)> <cutoff freq(Hz)> <output wav file>

    Argument Overview

    <input wav file>

    入力WAVファイル名

    <lpf/hpf>

    フィルタの種類を指定
    lpf:ローパスフィルタ
    hpf:ハイパスフィルタ

    <transition width(Hz)>

    遷移帯域幅
    この値が小さければ小さいほど処理が重く、メモリが多く消費される代わりにフィルタのカットオフが急峻になります。
    反対に大きいほど、処理が軽くメモリ消費量も少ない代わりに、カットオフがなだらかになります。

    <cutoff freq(Hz)>

    フィルタのカットオフ周波数

    <output wav file>

    フィルタ結果の出力先ファイル名(WAV形式で保存されます)

    もしくは

  • nsh> fir <input wav file> <bpf/bef> <transition width(Hz)> <cutoff1 freq(Hz)> <cutoff2 freq(Hz)> <output wav file>

    Argument Overview

    <input wav file>

    入力WAVファイル名

    <bpf/bef>

    フィルタの種類を指定
    bpf:帯域通過フィルタ
    bef:帯域停止フィルタ

    <transition width(Hz)>

    遷移帯域幅
    この値が小さければ小さいほど処理が重く、メモリが多く消費される代わりにフィルタのカットオフが急峻になります。
    反対に大きいほど、処理が軽くメモリ消費量も少ない代わりに、カットオフがなだらかになります。

    <cutoff1 freq(Hz)>

    低域側のフィルタのカットオフ周波数

    <cutoff2 freq(Hz)>

    高域側のフィルタのカットオフ周波数

    <output wav file>

    フィルタ結果の出力先ファイル名(WAV形式で保存されます)

事前準備でSDカードにコピーしたWAVファイル(仮に、 input.wav とします)に対して、5kHzのローパスフィルタ(遷移帯域幅3kHz)をかけて、output_lpf.wavというファイルに結果を保存したい場合、

nsh> fir /mnt/sd0/input.wav lpf 3000 5000 /mnt/sd0/output_lpf.wav

とします。
また、5kHz〜15kHzの帯域通過フィルタ(遷移帯域幅3kHz)をかけて、output_bpf.wavというファイルに結果を保存したい場合、

nsh> fir /mnt/sd0/input.wav bpf 3000 5000 15000 /mnt/sd0/output_bpf.wav

とします。

SpresenseのターミナルからSDカードのファイルにアクセスする場合、そのマウントポイントは、 /mnt/sd0 になります。
そのため、SDカードにコピーした input.wav を指定する場合、 /mnt/sd0/input.wav と入力します。

あとは、 フィルタ処理した結果ファイルのPCへのコピー方法 を参考に結果ファイルをPCに書き出して、音を聴くことでフィルタ処理の結果を耳で確認することができます。

9.1.3.3. decimate コマンドの動作確認

decimateコマンドは、入力WAVファイルに対して、指定された間引き率でデータの間引きを行うコマンドになっています。

decimateコマンドのコマンド仕様は以下のようになります。

  • nsh> decimate <input wav file> <Decimation Factor> <TransitionWidth or TapNum> <output wav file>

    Argument Overview

    <input wav file>

    入力WAVファイル名

    <Decimation Factor>

    間引き率
    正の整数を入力します。

    <TransitionWidth or TapNum>

    遷移帯域幅 もしくは、Tap数
    内部で利用するFIRのローパスフィルタの遷移帯域幅もしくはTap数を指定します。
    値が1000未満であればTap数、1000以上であれば遷移帯域幅と解釈されます。
    また、0を指定した場合、フィルタなしで単にデータを間引くだけの処理を行ます。

    <output wav file>

    フィルタ結果の出力先ファイル名(WAV形式で保存されます)

例えば、入力のWAVファイル input.wav に対して、3kHzの遷移帯域幅のフィルタで、1/16にデータを間引き、結果をoutput_dec.wavに保存する場合、

nsh> decimate /mnt/sd0/input.wav 16 3000 /mnt/sd0/output_dec.wav

と入力します。

あとは、 フィルタ処理した結果ファイルのPCへのコピー方法 を参考に結果ファイルをPCに書き出して、音を聴くことでフィルタ処理の結果を耳で確認することができます。

9.1.3.4. envelope コマンドの動作確認

envelopeコマンドは、入力WAVファイルに対して、2kHz〜15kHzの帯域通過フィルタをかけたのち、その絶対値を取り、その後1kHzのローパスフィルタをかける処理を行う一連の処理を行い、結果を指定したファイルにWAV形式で書き出すコマンドになっています。
この処理は、機械部品の劣化によるインパルス振動を安定して検波する際に用いられる手法の一つです。

H関数に関しては以下のサイトが参考になるかと思います。
https://www.imv.co.jp/products/vibrograph/watch/info/h_kansu.php

envelopeコマンドのコマンド仕様は以下のようになります。

  • nsh> envelope <input wav file> <output wav file>

    Argument Overview

    <input wav file>

    入力WAVファイル名

    <output wav file>

    結果の出力先ファイル名(WAV形式で保存されます)

入力のWAVファイル input.wav に対して、envelope処理を行、結果をoutput_env.wavに保存する場合、

nsh> envelope /mnt/sd0/input.wav /mnt/sd0/output_env.wav

と入力します。

あとは、 フィルタ処理した結果ファイルのPCへのコピー方法 を参考に結果ファイルをPCに書き出して、波形データを表示することで、envelope処理の結果を見ることができるようになります。

9.1.3.5. フィルタ処理した結果ファイルのPCへのコピー方法

各コマンドを実行した結果のファイルを取り出すために、再度SDカードをPCにマウントし、ファイルをPCにコピーします。

  1. USBマスストレージでPCにマウントします。

    NuttShell上で、 msconn と入力し、USBマスストレージを有効にします。

    nsh> msconn

    これで、Spresense 拡張ボードに挿入されているSDカードが、再度PC上にストレージデバイスとして接続されます。

  2. SDカードに保存されているコマンドの結果ファイルをPCにコピーします。

    PC上で接続されたストレージデバイスを開いて、fir/decimate/envelopeの各コマンドの実行で得られた結果のWAVファイルをPCにコピーします。

  3. USBマスストレージをアンマウントします。

    コピーが終わったら、NuttShellプロンプト上で、 msdis を実行して、PCのストレージデバイス接続を終了しておきます。

    nsh> msdis

これで、PCにコピーした結果ファイルを再生したり任意の波形表示ツールを使って表示することで、コマンドの実行結果を確認することができます。

10. Network チュートリアル

10.1. ftp サンプルアプリケーション

この章では、NuttXの標準サンプルである、ftpc及びftpdに関するサンプルアプリケーションの動作手順を示します。

10.1.1. ソースコードパス

このサンプルのソースコードは、

  • FTP Client側: sdk/apps/examples/ftpc

  • FTP Server側: sdk/apps/examples/ftpd

にそれぞれ入っています。

10.1.2. 動作環境

このサンプルを動作させるには、以下のハードウェアを使う前提となっています。 サーバ用とクライアント用の2セット用意するか、サーバ用に1セットとクライアント用にftp clientがインストールされたPCを1台用意する必要があります。

Spresense Main Board
IDY Wi-Fi Add-on Board iS110B

10.1.3. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/ftp を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/ftp
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

    Spresenseをもう1台使う場合はそちらにも同じnuttx.spkを書き込んでください。

10.1.4. 動作確認

シリアルターミナルを開いて、Wi-Fiに接続し、ftpd_start コマンドを実行して、 クライアントの接続を待ちます。 この例では、SpresenseのSPI Flash上に適当なファイル(test_ftp.txt)を作成して、 それをクライアントが取得(get)し、また、PC側からSpresenseに対して適当なファイルを送信(put)する動作を確認します。

  1. シリアルターミナルを起動します。

    下記の例では、シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定してminicomで接続しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShellでSPI Flash上に適当なテキストファイルを作成します。

    以下のコマンドを叩いて、test_ftp.txtというファイルを作成します。

    nsh> echo This is Spresense FTPD > /mnt/spif/test_ftp.txt
    nsh> echo Everything is OK. >> /mnt/spif/test_ftp.txt

    catコマンドを使って、正しくファイルが作られているかを確認します。

    nsh> cat /mnt/spif/test_ftp.txt
    This is Spresense FTPD
    Everything is OK.
  3. NuttShell から Wi-Fi接続を行い、 ftpd_start コマンドを実行します。

    実機をAPモードでWi-Fiの設定を行い、ftpd_startでFTPデーモンを起動してClientからの接続待ち状態にします。 下記の例では、APモードでWi-Fiを起動し、SSIDをspresense_net、パスワードを0123456789としています。 ifconfigコマンドでIPアドレスを確認して、FTPデーモンを起動します。

    nsh> gs2200m -a 1 spresense_net 0123456789 &
    nsh> sleep 5
    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 3c:95:09:00:56:49 at UP
            inet addr:192.168.11.1 DRaddr:192.168.11.1 Mask:255.255.255.0
    nsh> ftpd_start
    Initializing the network
    Starting the FTP daemon
    FTP daemon [10] started
    Adding accounts:
    1. Root account: USER=root PASSWORD=abc123 HOME=(none)
    2. User account: USER=ftp PASSWORD=(none) HOME=(none)
    3. User account: USER=anonymous PASSWORD=(none) HOME=(none)

    このftpdのサンプルでは、上記3つのアカウントが登録されています。これらのアカウント情報は、クライアントから接続する際に必要になります。

  4. PCからSpresenseのFTPサーバに接続します。

    上記の状態でPC側からSpresenseと同一のSSIDにWi-Fiを接続し、ftpコマンドでSpresense側に接続します。
    以下、Spresense側のIPアドレスが192.168.11.1として説明します。お使いの環境に合わせて適宜読み替えてください。 PCから接続すると、ユーザ名とパスワードが要求されるので、上記の3つのアカウントから好きなものを選び入力して接続を行なってください。以下の例では、rootアカウントを利用しています。
    (なお、お使いのPC環境によって、メッセージの出方は異なります)

    ftp 192.168.11.1
    Connected to 192.168.11.1.
    220 NuttX FTP Server
    Name (192.168.11.1:user): root
    331 Password required for root
    Password:
    230 Login successful.
    Remote system type is UNIX.
    Using binary mode to transfer files.
    ftp>
    

    あとは通常のftpのコマンドでファイルを取り出したり書き込んだりすることが出来ます。 まず、先ほど作成した/mnt/spif/test_ftp.txtを取得してみます。

    ftp> cd /mnt/spif
    250 CWD command successful
    ftp> get test_ftp.txt
    local: test_ftp.txt remote: test_ftp.txt
    229 Entering Extended Passive Mode (|||50000|).
    150 Opening data connection
    100% |**************************************************************************************************************************|    41        0.50 KiB/s    00:00 ETA
    226 Transfer complete
    41 bytes received in 00:00 (0.50 KiB/s)

    無事に転送が成功すると、上記のようなメッセージがPC側に出て来ます。
    FTPクライアントアプリを終了し、test_ftp.txtがあるか、中身が同じものかを確認します。

    ftp> quit
    221 Good-bye
    
    $ ls test_ftp.txt
    test_ftp.txt
    
    $ cat test_ftp.txt
    This is Spresense FTPD
    Everything is OK.

    無事に取得が出来たことがわかります。
    続いて、ファイルの書き込みを行なってみます。 まず、PC側でファイルを準備します。(ファイル名:test_pc.txt)

    echo "This is Host PC." > test_pc.txt
    echo "Nothing wrong with me." >> test_pc.txt
    cat test_pc.txt
    This is Host PC.
    Nothing wrong with me
    

    再度FTPクライアントを接続し、作成したファイルをputコマンドでSpresenseに転送します。

    ftp 192.168.11.1
    Connected to 192.168.11.1.
    220 NuttX FTP Server
    Name (192.168.11.1:user): root
    331 Password required for root
    Password:
    230 Login successful.
    Remote system type is UNIX.
    Using binary mode to transfer files.
    
    ftp> put test_pc.txt /mnt/spif/test_pc.txt
    local: test_pc.txt remote: /mnt/spif/test_pc.txt
    229 Entering Extended Passive Mode (|||50000|).
    150 Opening data connection
    100% |**************************************************************************************************************************|    40       63.61 KiB/s    00:00 ETA
    226 Transfer complete
    40 bytes sent in 00:00 (0.84 KiB/s)
    

    無事に転送が完了すると上記のようなメッセージがPC側で表示されます。
    Spresense側で転送されたファイルを見てみます。
    Spresenseに接続しているminicomターミナル上で以下のように確認が出来ます。 (nsh> というプロンプトが見えない場合、Enterキーを押せば出て来ます。)

    nsh> cat /mnt/spif/test_pc.txt
    This is Host PC.
    Nothing wrong with me.
    nsh>
  5. もう1台のSpresenseからクライアント接続する場合

    もう1台のSpresenseからクライアント接続を行う場合を説明します。
    サーバ側のSpresenseは上記のままで、Client側のSpresenseにminicomで接続し、Wi-Fi接続します。 サーバ側のSpresense同様にminicomでSpresenseのターミナルに接続してください。この例では、シリアルポートとして /dev/ttyUSB1 を、baudrate として 115200 bps を設定してminicomで接続しています。

    minicom -D /dev/ttyUSB1 -b 115200
    

    続いて、Wi-Fiに接続後、ftpcコマンドでサーバに接続します。

    nsh> gs2200m spresense_net 0123456789 &
    nsh> sleep 5
    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 3c:95:09:00:56:49 at UP
            inet addr:192.168.11.2 DRaddr:192.168.11.1 Mask:255.255.255.0
    nsh>
    nsh> ftpc 192.168.11.1
    NuttX FTP Client:

    NuttX標準のftpクライアントでは、特にプロンプト等は表示されません。
    この状態でログインを行います。この例では、アカウントrootでログインしています。

    login root abc123

    特になんの応答もありませんが、これでログインは成功します。
    先ほどPCで取得したサーバ側のSpreseneにある、/mnt/spif/test_ftp.txtを取得してみます。

    get /mnt/spif/test_ftp.txt /mnt/spif/test_ftp.txt

    login同様になんの応答もありませんが、これでファイルの取得が成功しています。
    ftpcコマンドから抜けて、ファイルが取得出来たかを確認します。

    quit
    nfc> Exiting...
    
    nsh> cat /mnt/spif/test_ftp.txt
    This is Spresense FTPD
    Everything is OK.

    無事に転送が出来ていれば、上記のようにサーバ側で作成したファイルを見ることが出来ます。

10.1.5. Tips 既存のWi-Fiネットワークに接続する場合

場合によっては、APモードではなく、Spresenseを既存でお使いのWi-Fiネットワークに接続したい場合もあるかと思います。 その場合は、実機側でのコマンド入力を以下のようにしてください。

nsh> gs2200m <接続したいSSID> <SSIDのパスワード> &
nsh> sleep 5
nsh> ifconfig
eth0    Link encap:Ethernet HWaddr 3c:95:09:00:56:49 at UP
        inet addr:XXX.XXX.XXX.XXX DRaddr:XXX.XXX.XXX.XXX Mask:XXX.XXX.XXX.XXX
nsh> ftpd_start
Initializing the network
Starting the FTP daemon
FTP daemon [10] started
Adding accounts:
1. Root account: USER=root PASSWORD=abc123 HOME=(none)
2. User account: USER=ftp PASSWORD=(none) HOME=(none)
3. User account: USER=anonymous PASSWORD=(none) HOME=(none)

IPアドレスやネットマスクは、そのSSIDによって割り振られ、ifconfigで確認できます。

10.2. tcpecho サンプルアプリケーション

この章では、NuttXの標準サンプルである、tcpechoに関するサンプルアプリケーションの動作手順を示します。 TCPのシンプルなエコーサーバのサンプルになります。

10.2.1. ソースコードパス

このサンプルのソースコードは、 sdk/apps/examples/tcpecho 以下にあります。

10.2.2. 動作環境

このサンプルを動作させるには、以下のハードウェアを使う前提となっています。

Spresense Main Board
IDY Wi-Fi Add-on Board iS110B
ホストPC (telnetクライアントがインストールされていること)

10.2.3. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/tcpecho を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/tcpecho
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

10.2.4. 動作確認

シリアルターミナルを開いて、Wi-Fiに接続し、tcpecho コマンドを実行して、クライアントから接続してechoバックを確認します。

  1. シリアルターミナルを起動します。

    まず、シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定してminicomで接続しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から tcpecho コマンドを実行します。

    実機をAPモードでWi-Fiの設定を行い、tcpechoを起動してClientからの接続待ち状態にします。 下記の例では、APモードでWi-Fiを起動し、SSIDをspresense_net、パスワードを0123456789としています。 ifconfigコマンドでIPアドレスを確認して、tcpechoを起動します。

    nsh> gs2200m -a 1 spresense_net 0123456789 &
    nsh> sleep 5
    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 3c:95:09:00:56:49 at UP
            inet addr:192.168.11.1 DRaddr:192.168.11.1 Mask:255.255.255.0
    nsh> tcpecho

    この状態でPC側から上記のSSIDにWi-Fiを接続し、telnetで192.168.11.1のポート80番に接続します。

    telnet 192.168.11.1 80
    Trying 192.168.11.1...
    Connected to 192.168.11.1.
    Escape character is '^]'.
    

    後はPCで適当な文字を打ってEnterキーを打てば、エコーバックが帰ってきます。

    hello
    hello        <- エコーバック文字列
    Spresense
    Spresense    <- エコーバック文字列

10.3. tcpblaster サンプルアプリケーション

この章では、NuttXの標準サンプルである、tcpblasterに関するサンプルアプリケーションの動作手順を示します。 TCPで接続したserver / client間でデータの送受信を行い、その転送レートを計測するサンプルです。

10.3.1. ソースコードパス

このサンプルのソースコードは、 sdk/apps/examples/tcpblaster 以下にあります。

10.3.2. 動作環境

このサンプルを動作させるには、以下のハードウェアを2セット使う前提となっています。 なお、対向をPCにして、1セットのみで動作させることも可能です。 最後にTipsとして、コンフィグの変更方法を載せています。

Spresense Main Board
IDY Wi-Fi Add-on Board iS110B

10.3.3. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/tcpblaster を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/tcpblaster
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    2つの機材に同じプログラムを焼き込みます。 この例では シリアルポートとして /dev/ttyUSB0, /dev/ttyUSB1 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    tools/flash.sh -c /dev/ttyUSB1 -b 500000 nuttx.spk
    

10.3.4. 動作確認

シリアルターミナルを開いて、Wi-Fiに接続し、tcpserver / tcpclient コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、2つのminicom ターミナルを使用する例です。 まず、一つ目のターミナルから、シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定してminicomで接続しています。

    minicom -D /dev/ttyUSB0 -b 115200
    

    まず、二つ目も同様に、シリアルポートを /dev/ttyUSB1 として接続しています。

    minicom -D /dev/ttyUSB1 -b 115200
    
  2. それぞれのNuttShell から tcpserver もしくは tcpclient コマンドを実行します。

    まず、片方の実機をAPモードでWi-Fiの設定を行います。 下記の例では、APモードでWi-Fiを起動し、SSIDをspresense_net、パスワードを0123456789としています。 Wi-Fiモジュールが正しく起動されると、IPアドレスが設定されるので、それを確認します。 ifconfigコマンドで、下記のように、IPアドレスが割り当てられます。

    ★ターミナル1
    nsh> gs2200m -a 1 spresense_net 0123456789 &
    nsh> sleep 5
    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 3c:95:09:00:56:49 at UP
            inet addr:192.168.11.1 DRaddr:192.168.11.1 Mask:255.255.255.0

    続いてもう一方のminicomのターミナルに対して、上記で作成したAPにSTAモードで接続し、同様に割り当てられたIPアドレスを確認します。 gs2200mにてWi-Fiの設定を行なった後、5秒程度待ちます。これは、Wi-Fi APからのDHCPでのIPアドレスが付与されるなどを待つのが目的です。 無事に接続が上手く出来ると、下記のように、デフォルトルーターが上記APのアドレスになって、自己のIPアドレスが割り振られます。

    ★ターミナル2
    nsh> gs2200m spresense_net 0123456789 &
    nsh> sleep 5
    nsh> ifconfig
    eth0    Link encap:Ethernet HWaddr 3c:95:09:00:64:91 at UP
            inet addr:192.168.11.2 DRaddr:192.168.11.1 Mask:255.255.255.0

    この状態で、tcpserverを起動してClientからの接続待ち状態にします。

    ★ターミナル1
    nsh> tcpserver
    Binding to IPv4 Address: 00000000
    server: Accepting connections on port 5471

    続いて、tcpclientを起動します。引数に接続先のIPアドレスを設定します。 ターミナル1側のIPアドレス(この例では、192.168.11.1)を設定しています。 正しく動作すると、データの送受信が開始され、通信レートが算出されて表示されます。

    ★ターミナル2
    nsh> tcpclient 192.168.11.1
    Connecting to IPv4 Address: 010ba8c0
    client: Connected
    Sent 50 buffers:     50.0 Kb (avg   1.0 Kb) in   0.25 Sec (  195.1 Kb/Sec)
    Sent 50 buffers:     50.0 Kb (avg   1.0 Kb) in   0.16 Sec (  308.8 Kb/Sec)
    Sent 50 buffers:     50.0 Kb (avg   1.0 Kb) in   0.15 Sec (  329.8 Kb/Sec)
    Sent 50 buffers:     50.0 Kb (avg   1.0 Kb) in   0.17 Sec (  282.6 Kb/Sec)
    Sent 50 buffers:     50.0 Kb (avg   1.0 Kb) in   0.17 Sec (  286.9 Kb/Sec)
10.3.4.1. Tips 対向機をPCにする方法

この例では2セットのSpresenseを用いて速度計測をしていましたが、 片方をPCにすることも可能です。

その場合、上記コンフィグ手順まで終わった状態で、 menuconfigを開いて、以下のようにビルド設定を変更します。 spresense/sdk以下で、

make menuconfig

とし、

Application Configuration ->
  Examples ->
    TCP Performance Test

にある、

Second endpoint is a target

のチェックを外します。 これで、一旦cleanしてから再度ビルドを行います。

make clean
make

無事にビルドが完了すると、ホスト側のプログラムとして、tcpclient が、sdk/apps/examples/tcpblasterの下にできています。 この使い方は、上記のtcpclientと同様です。 Spresense側でtcpserverを立ち上げておき、PCからtcpclientで接続することで、 動作可能です。

11. Graphics チュートリアル

11.1. NX サンプルアプリケーション

この章では、NuttXのグラフィックスシステムである、NXに関するサンプルアプリケーションの動作手順を示します。 NuttXのグラフィックスサブシステムに関する詳細は、 NuttX NX Graphic Subsystem を参照してください。

Spresenseで動作確認をしているNXに関するサンプルコードとそれぞれのソースコードのディレクトリパスは、 下記の様になります。

表 7. NXサンプルコード
サンプル名 ソースコードパス デフォルトコンフィグ名 概要

fb

sdk/apps/examples/fb

examples/fb

フレームバッファをダイレクトに操作するサンプル

nx

sdk/apps/examples/nx

examples/nx

NXのWindowシステムの描画に関するサンプル

nxdemo

sdk/apps/examples/nxdemo

examples/nxdemo

NXの描画に関するサンプル

nxhello

sdk/apps/examples/nxhello

examples/nxhello

NXのテキスト描画に関するサンプル

nximage

sdk/apps/examples/nximage

examples/nximage

NXのイメージ描画に関するサンプル

nxlines

sdk/apps/examples/nxlines

examples/nxlines

NXの直線描画に関するサンプル

nxtext

sdk/apps/examples/nxtext

examples/nxtext

NXのテキスト描画とポップアップウィンドウの描画に関するサンプル

11.1.1. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に 上記の表の デフォルトコンフィグ名 に記載されている値を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py <デフォルトコンフィグ名>
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

11.1.2. 動作環境

このサンプルを動作させるには、以下のハードウェアを使う前提となっています。

Spresense Main Board
Spresense Extension Board
Arduino UNO LCD Connector board
ILI9341 2.2inch LCD

11.1.3. 動作確認

シリアルターミナルを開いて、nx コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
11.1.3.1. NuttShell から それぞれのコマンドを実行します。
  1. fb サンプルの場合

    以下の様なメッセージとともに、LCDディスプレイに様々な色の矩形が描画されます。

    nsh> fb
    VideoInfo:
          fmt: 11
         xres: 320
         yres: 240
      nplanes: 1
    PlaneInfo (plane 0):
        fbmem: 0xd0353c0
        fblen: 153600
       stride: 640
      display: 0
          bpp: 16
    Mapped FB: 0xd0353c0
     0: (  0,  0) (319,239)
     1: ( 29, 21) (290,218)
     2: ( 58, 42) (261,197)
     3: ( 87, 63) (232,176)
     4: (116, 84) (203,155)
     5: (145,105) (174,134)
    Test finished
  2. nx サンプルの場合

    以下の様なメッセージとともに、LCDディスプレイにWindowが描画されます。

    nsh> nx
    nx_main: NX handle=0xd032050
    nx_main: Set background color=8087790
    nx_main: Create window #1
    nx_main: hwnd1=0xd03aeb0
    nx_main: Screen resolution (319,239)
    nx_main: Set window #1 size to (159,119)
    nx_main: Sleeping
    
    nxeg_position1: hwnd=0xd03aeb0 size=(159,119) pos=(4,4) bounds={(0,0),(319,239)}
    nxeg_position1: hwnd=0xd03aeb0 size=(159,119) pos=(4,4) bounds={(0,0),(319,239)}
    nxeg_redraw1: hwnd=0xd03aeb0 rect={(0,0),(158,118)} more=false
    nx_main: Set window #1 position to (39,29)
    nx_main: Sleeping
    
    nxeg_position1: hwnd=0xd03aeb0 size=(159,119) pos=(39,29) bounds={(0,0),(319,239)}
    nxeg_redraw1: hwnd=0xd03aeb0 rect={(0,0),(158,118)} more=false
    nx_main: Add toolbar to window #1
    nx_main: Sleeping
    
    nxeg_redraw1: hwnd=0xd03aeb0 rect={(0,0),(158,102)} more=false
    nxeg_tbredraw1: hwnd=0xd03aeb0 rect={(0,0),(158,15)} more=false
    nx_main: Create window #2
    nx_main: hwnd2=0xd03af10
    nx_main: Sleeping
    
    nxeg_position2: hwnd=0xd03af10 size=(0,0) pos=(0,4) bounds={(0,0),(319,239)}
    nx_main: Set hwnd2 size to (159,119)
    nx_main: Sleeping
    
    nxeg_position2: hwnd=0xd03af10 size=(159,119) pos=(4,4) bounds={(0,0),(319,239)}
    nxeg_redraw2: hwnd=0xd03af10 rect={(0,0),(158,118)} more=false
    nx_main: Set hwnd2 position to (121,91)
    nx_main: Sleeping
    
    nxeg_position2: hwnd=0xd03af10 size=(159,119) pos=(121,91) bounds={(0,0),(319,239)}
    nxeg_redraw2: hwnd=0xd03af10 rect={(0,0),(158,118)} more=false
    nxeg_redraw1: hwnd=0xd03aeb0 rect={(0,0),(158,41)} more=false
    nxeg_tbredraw1: hwnd=0xd03aeb0 rect={(0,0),(158,15)} more=false
    nxeg_redraw1: hwnd=0xd03aeb0 rect={(0,42),(77,102)} more=false
    nx_main: Add toolbar to window #2
    nx_main: Sleeping
    
    nxeg_redraw2: hwnd=0xd03af10 rect={(0,0),(158,102)} more=false
    nxeg_tbredraw2: hwnd=0xd03af10 rect={(0,0),(158,15)} more=false
    nx_main: Lower window #2
    nx_main: Sleeping
    
    nxeg_redraw1: hwnd=0xd03aeb0 rect={(78,42),(158,102)} more=false
    nx_main: Raise window #2
    nx_main: Sleeping
    
    nxeg_redraw2: hwnd=0xd03af10 rect={(0,0),(158,102)} more=false
    nxeg_tbredraw2: hwnd=0xd03af10 rect={(0,0),(158,15)} more=false
    nx_main: Close window #2
    nx_main: Close window #1
    nx_main: Disconnect from the server
    nxeg_redraw1: hwnd=0xd03aeb0 rect={(78,42),(158,102)} more=false
    nx_listenerthread: Lost server connection: 117
  3. nxdemo サンプルの場合

    以下の様なメッセージがターミナルに表示され、LCDディスプレイに様々な模様が描画されます。

    nsh> nxdemo
    nxdemo_listener: Connected
    nxdemo_main: NX handle=0xd031440
    nxdemo_main: Screen resolution (320,240)
    nxdemo_main: Disconnect from the server
    nxdemo_listener: Lost server connection: 117
  4. nxhello サンプルの場合

    以下の様なメッセージがターミナルに表示され、LCDディスプレイの中央に Hello, World! という文字が描画されます。

    nsh> nxhello
    nxhello_main: NX handle=0xd031440
    nxhello_main: Set background color=31581
    nxhello_main: Screen resolution (320,240)
    nxhello_hello: Position (114,105)
    nxhello_main: Disconnect from the server
    nxhello_listener: Lost server connection: 117
  5. nximage サンプルの場合

    このサンプルでは、コマンドが nxhello となります。 実行すると以下の様なメッセージがターミナルに表示され、LCDディスプレイの中央にNuttXのロゴマークが描画されます。

    nsh> nxhello
    nximage_main: NX handle=0xd033d80
    nximage_main: Set background color=0
    nximage_listener: Connected
    nximage_main: Screen resolution (320,240)
    nximage_main: Disconnect from the server
    nximage_listener: Lost server connection: 117
  6. nxlines サンプルの場合

    以下の様なメッセージがターミナルに表示され、LCDディスプレイに円と円の中央に線が描画され続けます。

    nsh> nxlines
    Angle: 0002bfaa vector: (184,60)->(136,180)
    Angle: 0002f1ed vector: (172,57)->(148,183)
    Angle: 00032430 vector: (160,56)->(160,184)
    Angle: 00035673 vector: (147,57)->(173,183)
    Angle: 000388b6 vector: (135,60)->(185,180)
    Angle: 0003baf9 vector: (124,66)->(196,174)
    Angle: 0003ed3c vector: (114,74)->(206,166)
    Angle: 00041f7f vector: (106,84)->(214,156)
    Angle: 000451c2 vector: (100,95)->(220,145)
    Angle: 00048405 vector: (97,107)->(223,133)
    Angle: 0004b648 vector: (96,119)->(224,121)
    Angle: 0004e88b vector: (97,132)->(223,108)
    Angle: 00051ace vector: (100,144)->(220,96)
    Angle: 00054d11 vector: (106,155)->(214,85)
    Angle: 00057f54 vector: (114,165)->(206,75)
  7. nxtext サンプルの場合

    以下の様なメッセージがターミナルに表示され、LCDディスプレイに様々な文字が描画されると共にポップアップ矩形が描画されます。

    nsh> nxtext
    nxtext_initialize: Starting NX server
    nxtext_main: NX handle=0xd032450
    nxtext_main: Set background color=31581
    nxtext_listener: Connected
    nxtext_main: Screen resolution (320,240)
    nxpu_open: Create pop-up
    nxpu_open: Set pop-up position to (22,99)
    nxtext_main: Close pop-up
    nxpu_open: Create pop-up
    nxpu_open: Set pop-up position to (90,46)
    nxtext_main: Close pop-up
    nxpu_open: Create pop-up
    nxpu_open: Set pop-up position to (171,136)
    nxtext_main: Close pop-up
    nxpu_open: Create pop-up
    nxpu_open: Set pop-up position to (42,164)
    nxtext_main: Close pop-up
    nxpu_open: Create pop-up
    nxpu_open: Set pop-up position to (100,16)
    nxtext_main: Close pop-up
    nxpu_open: Create pop-up
    nxpu_open: Set pop-up position to (132,67)
    nxtext_main: Close pop-up
    nxpu_open: Create pop-up

11.1.4. Tips LCD画面のオリエンテーションの変更方法

作成されるアプリケーションによって、LCD画面の前後左右を変更したい場合、 以下のコンフィグを変更することで、LCDのオリエンテーションを変更することが可能です。 tools/config.py -m でコンフィグメニューを開き、下記の階層にある、 LCD Orientation の設定値を変更してください。

Device Drivers ->
  LCD Driver Support ->
    Graphic LCD Driver Support ->
      LCD driver selection ->
        LCD Orientation ()

12. FileSystem チュートリアル

12.1. SmartFS

SPI-Flash のファイルシステムに使用している SmartFS について説明します。

SmartFS ファイルシステムの詳細については以下のNuttX Wikiを参照してください。

12.1.1. 初期化&マウント処理

起動時に呼ばれる cxd56_bringup() 関数から board_flash_initialize() を呼び出します。

File: nuttx/boards/arm/cxd56xx/spresense/src/cxd56_bringup.c

#ifdef CONFIG_CXD56_SFC
  ret = board_flash_initialize();
  if (ret < 0)
    {
      _err("ERROR: Failed to initialize SPI-Flash. %d\n", errno);
    }
#endif

board_flash_initialize() 関数の中で、SPI-Flash を SmartFS ファイルシステムとして 初期化し、/mnt/spif ディレクトリへマウントします。

File: nuttx/boards/arm/cxd56xx/common/src/cxd56_flash.c

int board_flash_initialize(void)
{
  int ret;
  FAR struct mtd_dev_s *mtd;

  mtd = cxd56_sfc_initialize(); (1)
  if (!mtd)
    {
      ferr("ERROR: Failed to initialize SFC. %d\n ", ret);
      return -ENODEV;
    }

  /* use the FTL layer to wrap the MTD driver as a block driver */

  ret = ftl_initialize(CONFIG_SFC_DEVNO, mtd);
  if (ret < 0)
    {
      ferr("ERROR: Initializing the FTL layer: %d\n", ret);
      return ret;
    }

#if defined(CONFIG_FS_SMARTFS)
  /* Initialize to provide SMARTFS on the MTD interface */

  ret = smart_initialize(CONFIG_SFC_DEVNO, mtd, NULL); (2)
  if (ret < 0)
    {
      ferr("ERROR: SmartFS initialization failed: %d\n", ret);
      return ret;
    }

  ret = mount("/dev/smart0d1", "/mnt/spif", "smartfs", 0, NULL); (3)
  if (ret < 0)
    {
      ferr("ERROR: Failed to mount the SmartFS volume: %d\n", errno);
      return ret;
    }
#elif defined(CONFIG_FS_NXFFS)
1 Memory Technology Device (MTD) デバイスとして登録します。
2 MTD デバイスを SmartFS ファイルシステムとして初期化します。
3 /dev/smart0d1 デバイスファイルを /mnt/spif へマウントします。

12.1.2. コンフィグレーション

SmartFS を使用するにあたり、いくつかのコンフィグレーションが存在します。

SDK としてデフォルトで設定している推奨構成(sdk/configs/default/defconfig)を以下に示します。

Configuration Value Description

CONFIG_FS_SMARTFS

y

SmartFS ファイルシステムを有効にします

CONFIG_SMARTFS_ERASEDSTATE=0xff

0xff

SPI-Flashの値が0xFFのときにErase状態を意味します。

CONFIG_SMARTFS_MAXNAMLEN

30

ファイル名の最大長は30文字です。

CONFIG_SMARTFS_MULTI_ROOT_DIRS

y

マルチルートディレクトリをサポートしていますが、 デフォルトでは 1 つのルートディレクトリエントリを使用しています。

CONFIG_MTD_SMART

y

MTD デバイスとして SmartFS を使用します

CONFIG_MTD_SMART_SECTOR_SIZE

4096

1 セクタのサイズは 4096 バイトです。ファイルの最小単位として 1 セクタ分を使用します。 このサイズを小さくすると管理できるファイル数は増えますが、 小さくしすぎるとファイルのサーチ処理に時間がかかるようになります。

CONFIG_MTD_SMART_WEAR_LEVEL

n

これを有効にすると Read-only ファイルについても定期的にセクタ移動を行います。 Read-only ファイルについても移動中に電源がOffされてファイルが消去される可能性があるため、 途中で電源断が発生し得るシステムの場合は Disable することを推奨します。

CONFIG_MTD_SMART_ENABLE_CRC

y

CRCを使用して書き込み不良のチェックを行います。

CONFIG_MTD_SMART_FSCK

y

ファイル操作中に電源断が発生した場合など、ファイルシステムとして不整合のある状態が発生します。 これを有効にすると、起動時にファイルシステムの整合性をチェックし、ファイルとして成立していない ファイルを削除してファイルシステムを整合性の取れた状態に保ちます。

CONFIG_MTD_SMART_MINIMIZE_RAM

n

論物変換セクタをキャッシュする操作ですが、有効にするとアクセス速度が低下するため デフォルトではdisableしています。

12.1.3. 動作確認

NuttShell プロンプトから df コマンドによって Filesystem を確認することができます。

nsh> df -h
  Filesystem    Size      Used  Available Mounted on
  smartfs         4M       68K      4028K /mnt/spif
  procfs          0B        0B         0B /proc

/mnt/spifsmartfs ファイルシステムとしてマウントされていること、 トータル 4Mバイトのサイズのうち、使用サイズ (Used)、空きサイズ (Available) を確認することができます。

ファイルを作成する
nsh> echo "This is a test file." > /mnt/spif/test.txt
作成されたファイルのサイズを確認する
nsh> ls -l /mnt/spif
/mnt/spif:
 -rw-rw-rw-      21 test.txt
ファイルの内容を確認する
nsh> cat /mnt/spif/test.txt
This is a test file.

プログラムからファイル操作を行う場合は、 open/read/write/closeといった低水準入出力関数や fopen/fread/fwrite/fcloseといったファイル入出力関数を使うことができます。

12.1.4. フォーマット手順

SPI-Flash は工場出荷時に SmartFS ファイルシステムとして使用できるように初期化されています。 SPI-Flash をクリーンアップしたいときには mksmartfs コマンドを利用することができます。

Usage
nsh> help mksmartfs
mksmartfs usage:  mksmartfs [-s <sector-size>] [-f] <path> [<num-root-directories>]
Example
nsh> mksmartfs -s 4096 -f /dev/smart0d1 1

mksmartfs コマンドを実行後に、root directory 領域として数セクタが予約され、残りを使用することが可能です。

nsh> df -h
  Filesystem    Size      Used  Available Mounted on
  smartfs         4M       28K      4068K /mnt/spif
  procfs          0B        0B         0B /proc

12.1.5. Flash 消去

起動時に /dev/smart0d1 デバイスファイルが見つからない場合や、 アプリケーションで使用している SPI-Flash 領域を完全に削除したい場合は、 flash_eraseall コマンドを利用することができます。

flash_eraseall コマンドを有効にするには、CONFIG_SYSTEM_FLASH_ERASEALL を有効にしてください。

Configuration
Application Configuration -> System Libraries and NSH Add-Ons
  FLASH Erase-all Command (CONFIG_SYSTEM_FLASH_ERASEALL)
Usage
nsh> flash_eraseall
usage: flash_eraseall flash_block_device
Command
nsh> flash_eraseall /dev/mtdblock0

SPI-Flash を削除した後に SmartFS として使用する場合は、 ボードを再起動した後に、前述したフォーマット手順を参考にしてください。

12.2. FAT ファイルシステム

SD カードおよび eMMC のファイルシステムに使用している FAT ファイルシステムについて説明します。

  • SD カード: 拡張ボードやLTE拡張ボードに SD カード用のスロットが搭載されています

  • eMMC: Spresense 用のeMMC拡張ボード をサポートしています

12.2.1. 初期化&マウント処理

起動時に呼ばれる cxd56_bringup() 関数から board_sdcard_initialize() や board_emmc_initialize() を呼び出して初期化を行います。

File: nuttx/boards/arm/cxd56xx/spresense/src/cxd56_bringup.c

SD カード

#ifdef CONFIG_CXD56_SDIO
  ret = board_sdcard_initialize();
  if (ret < 0)
    {
      _err("ERROR: Failed to initialize sdhci. \n");
    }
#endif

eMMC

#if defined(CONFIG_CXD56_EMMC) && !defined(CONFIG_CXD56_EMMC_LATE_INITIALIZE)
  /* Mount the eMMC block driver */

  ret = board_emmc_initialize();
  if (ret < 0)
    {
      _err("ERROR: Failed to initialize eMMC: %d\n", ret);
    }
#endif

SD カードの処理

board_sdcard_initialize() 関数の中で、 SD カードの挿抜検出をするための GPIO 割り込み設定を行います。 SD カードが挿入されている場合は、board_sdcard_enable() 関数が呼ばれ、 SD ホストコントローラを初期化し、SD カードを /mnt/sd0 ディレクトリへマウントします。 SD カードが抜かれた場合は、board_sdcard_disable() が呼ばれ、 SD ホストコントローラの終了処理を行い、/mnt/sd0 をアンマウントします。

File: nuttx/boards/arm/cxd56xx/spresense/src/cxd56_sdcard.c

int board_sdcard_initialize(void)
{
#ifdef CONFIG_MMCSD_HAVE_CARDDETECT
  /* Configure Interrupt pin with internal pull-up */

  cxd56_pin_config(PINCONF_SDIO_CD_GPIO);
  ret = cxd56_gpioint_config(PIN_SDIO_CD,
                             GPIOINT_PSEUDO_EDGE_FALL,
                             board_sdcard_detect_int,
                             NULL); (1)
    :
}

static void board_sdcard_enable(FAR void *arg)
{
    :
      g_sdhci.sdhci = cxd56_sdhci_initialize(0); (2)

      if (!stat("/dev/mmcsd0", &stat_sdio) == 0)
        {
          /* Now bind the SDHC interface to the MMC/SD driver */

          ret = mmcsd_slotinitialize(0, g_sdhci.sdhci);

          finfo("Successfully bound SDHC to the MMC/SD driver\n");
        }

      /* Handle the initial card state */

      cxd56_sdhci_mediachange(g_sdhci.sdhci);

      if (stat("/dev/mmcsd0", &stat_sdio) == 0)
        {
          if (S_ISBLK(stat_sdio.st_mode))
            {
              ret = mount("/dev/mmcsd0", "/mnt/sd0", "vfat", 0, NULL); (3)
            }
        }
    :
}

static void board_sdcard_disable(FAR void *arg)
{
      ret = umount("/mnt/sd0"); (4)

      cxd56_sdhci_finalize(0); (5)
}
1 SD カードの挿抜検出用の GPIO 割り込みを設定します。
2 SD ホストコントローラを初期化します。
3 /dev/mmcsd0 デバイスファイルを /mnt/sd0 へマウントします。
4 /mnt/sd0 をアンマウントします。
5 SD ホストコントローラの終了処理を実行します。

eMMCの処理

board_emmc_initialize() 関数の中で、 eMMC ドライバを初期化し、eMMC デバイスを /mnt/emmc ディレクトリへマウントします。 一方、終了処理は、board_emmc_finalize() 関数を呼び出すことでアンマウント処理を行います。

File: nuttx/boards/arm/cxd56xx/common/src/cxd56_emmcdev.c

int board_emmc_initialize(void)
{
  /* Power on the eMMC device */

  ret = board_power_control(POWER_EMMC, true); (1)
    :

  /* Initialize the eMMC device */

  ret = cxd56_emmcinitialize(); (2)
    :

  /* Mount the eMMC device */

  ret = nx_mount("/dev/emmc0", "/mnt/emmc", "vfat", 0, NULL); (3)
    :
}

int board_emmc_finalize(void)
{
  /* Un-mount the eMMC device */

  ret = nx_umount2("/mnt/emmc", MNT_DETACH); (4)
    :

  /* Uninitialize the eMMC device */

  ret = cxd56_emmcuninitialize(); (5)
    :

  /* Power off the eMMC device */

  ret = board_power_control(POWER_EMMC, false); (6)
    :
}
1 eMMC の電源を入れます。
2 eMMC ドライバを初期化します。
3 /dev/emmc0 デバイスファイルを /mnt/emmc へマウントします。
4 /mnt/emmc をアンマウントします。
5 eMMC ドライバの終了処理を実行します。
6 eMMC の電源を落とします。

12.2.2. コンフィグレーション

FAT ファイルシステムを使用するにあたり、いくつかのコンフィグレーション項目が存在します。

SDK としてデフォルトで設定しているコンフィグレーション値を以下に示します。

Configuration Value Description

CONFIG_CXD56_SDIO

y

SD カードの使用を有効にします

CONFIG_CXD56_EMMC

y

eMMC の使用を有効にします

CONFIG_CXD56_EMMC_POWER_PIN_XXX

y

eMMC デバイスの電源制御に使用するピンを選択します。デフォルトはPIN_I2S0_BCKを使用します。

CONFIG_FS_FAT

y

FAT ファイルシステムを有効にします

CONFIG_FAT_LCNAMES

y

ファイル名の大文字/小文字を区別します

CONFIG_FAT_LFN

y

FAT ロングファイルネームをサポートします

CONFIG_FAT_MAXFNAME

64

ファイル名の最大文字数を定義します

CONFIG_FS_FATTIME

y

ファイルのタイムスタンプ機能をサポートします。 RTC に現在時刻が設定されている場合 (正確には RTC が1980年1月1日以降のとき) に ファイルの作成日時及び更新日時を記録します。

12.2.3. ビルド手順

SD カードや eMMC を使用する際のコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    SD カードを使用する場合は引数に device/sdcard を、eMMC を使用する場合は引数に device/emmc を指定してコンフィグレーションを実行します。 両方を指定して同時に使用することも可能です。

    tools/config.py device/sdcard (SD カードを使用する場合)
    tools/config.py device/emmc   (eMMC を使用する場合)
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

12.2.4. 動作確認

NuttShell プロンプトから df コマンドによって Filesystem を確認することができます。

nsh> df -h
  Filesystem    Size      Used  Available Mounted on
  vfat           14G     2762M        11G /mnt/sd0
  smartfs         4M       28K      4068K /mnt/spif
  procfs          0B        0B         0B /proc

例えば、 /mnt/sd0vfat ファイルシステムとしてマウントされていることや、 トータルサイズ (Size) のうち、使用サイズ (Used)、空きサイズ (Available) を確認することができます。

RTCに時刻を設定する(ファイルのタイムスタンプ機能を使用する)
nsh> date -s "Feb 22 12:34:00 2021"
nsh> date
Feb 22 12:34:02 2021
ファイルを作成する
nsh> echo "This is a test file." > /mnt/sd0/test.txt
作成されたファイルのサイズを確認する
nsh> ls -l /mnt/sd0/test.txt
 -rw-rw-rw-      21 /mnt/sd0/test.txt
ファイルの内容を確認する
nsh> cat /mnt/sd0/test.txt
This is a test file.

PC から SD カード内のファイルのプロパティをみると、タイムスタンプが正しく設定されていることが確認できます。

プログラムからファイル操作を行う場合は、 open/read/write/closeといった低水準入出力関数や fopen/fread/fwrite/fcloseといったファイル入出力関数を使うことができます。

12.2.5. フォーマット手順

PC 上で SD メモリーカードフォーマッター等を用いて、FAT ファイルシステムのフォーマットを行ってください。

exFAT ファイルシステムはサポートされていません。

また、ボード上で SD カードや eMMC のフォーマットを行う場合は、mkfatfs コマンドを利用してください。

nsh> help mkfatfs
mkfatfs usage:  mkfatfs [-F <fatsize>] [-r <rootdirentries>] <block-driver>

FAT32 フォーマットの方法を以下に示します。

SD カード

nsh> mkfatfs -F 32 /dev/mmcsd0

eMMC

nsh> mkfatfs -F 32 /dev/emmc0

12.3. fsperf サンプルアプリケーション

ファイルシステムのread/writeパフォーマンスを測定するためのサンプルアプリケーション fsperf の使い方について説明します。

12.3.1. ビルド手順

コマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/fsperf を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/fsperf
    make
    

    eMMC デバイスを使用する場合は、コンフィグレーションの引数に device/emmc を追加してください。

    tools/config.py examples/fsperf device/emmc
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

12.3.2. 動作確認

NuttShell プロンプトから fsperf コマンドを実行した例を以下に示します。

引数に -f オプションとテストファイルのパスを指定して、fsperf コマンドを実行します。 指定するパスは、測定対象のメディアによって変更してください。

はじめに mkdir コマンドで "temp" ディレクトリを作成しています。

最後に rm コマンドを用いて、実行中に作成したテストファイル群をディレクトリごと削除しています。

SD カード

nsh> mkdir /mnt/sd0/temp
nsh> fsperf -f /mnt/sd0/temp/test
File access speed monitor!!
--- fwrite summary: Size:    1 [KB], Min:   4.064, Avg:   4.190, Max:   4.572 [Mbps]
--- fwrite summary: Size:    2 [KB], Min:   1.954, Avg:   6.010, Max:   7.877 [Mbps]
--- fwrite summary: Size:    4 [KB], Min:  14.028, Avg:  14.105, Max:  14.423 [Mbps]
--- fwrite summary: Size:    8 [KB], Min:  23.541, Avg:  23.704, Max:  24.095 [Mbps]
--- fwrite summary: Size:   16 [KB], Min:  34.713, Avg:  37.169, Max:  37.927 [Mbps]
--- fwrite summary: Size:   32 [KB], Min:  18.833, Avg:  28.825, Max:  37.407 [Mbps]
--- fwrite summary: Size:   64 [KB], Min:  21.817, Avg:  32.483, Max:  38.551 [Mbps]
--- fwrite summary: Size:  128 [KB], Min:  27.149, Avg:  32.367, Max:  37.708 [Mbps]
--- fwrite summary: Size:  256 [KB], Min:  27.026, Avg:  30.648, Max:  33.217 [Mbps]
--- fwrite summary: Size:  512 [KB], Min:  18.553, Avg:  25.683, Max:  30.892 [Mbps]
--- fwrite summary: Size: 1024 [KB], Min:  12.002, Avg:  22.746, Max:  29.715 [Mbps]
---  fread summary: Size:    1 [KB], Min:  17.067, Avg:  18.156, Max:  18.286 [Mbps]
---  fread summary: Size:    2 [KB], Min:  26.948, Avg:  27.235, Max:  28.445 [Mbps]
---  fread summary: Size:    4 [KB], Min:  39.385, Avg:  39.691, Max:  40.961 [Mbps]
---  fread summary: Size:    8 [KB], Min:  51.201, Avg:  51.201, Max:  51.201 [Mbps]
---  fread summary: Size:   16 [KB], Min:  57.691, Avg:  57.854, Max:  58.515 [Mbps]
---  fread summary: Size:   32 [KB], Min:  52.853, Avg:  53.058, Max:  53.196 [Mbps]
---  fread summary: Size:   64 [KB], Min:  57.288, Avg:  57.428, Max:  57.489 [Mbps]
---  fread summary: Size:  128 [KB], Min:  60.126, Avg:  60.192, Max:  60.236 [Mbps]
---  fread summary: Size:  256 [KB], Min:  60.739, Avg:  60.773, Max:  60.795 [Mbps]
---  fread summary: Size:  512 [KB], Min:  61.079, Avg:  61.080, Max:  61.089 [Mbps]
---  fread summary: Size: 1024 [KB], Min:  61.350, Avg:  61.363, Max:  61.364 [Mbps]
nsh> rm -r /mnt/sd0/temp

eMMC

nsh> mkdir /mnt/emmc/temp
nsh> fsperf -f /mnt/emmc/temp/test
File access speed monitor!!
--- fwrite summary: Size:    1 [KB], Min:   6.564, Avg:   7.211, Max:   8.000 [Mbps]
--- fwrite summary: Size:    2 [KB], Min:  11.637, Avg:  14.546, Max:  16.516 [Mbps]
--- fwrite summary: Size:    4 [KB], Min:  26.257, Avg:  27.602, Max:  28.445 [Mbps]
--- fwrite summary: Size:    8 [KB], Min:  43.575, Avg:  45.613, Max:  47.629 [Mbps]
--- fwrite summary: Size:   16 [KB], Min:  57.691, Avg:  63.211, Max:  71.861 [Mbps]
--- fwrite summary: Size:   32 [KB], Min:  67.149, Avg:  75.573, Max:  88.088 [Mbps]
--- fwrite summary: Size:   64 [KB], Min:  55.352, Avg:  76.922, Max:  94.707 [Mbps]
--- fwrite summary: Size:  128 [KB], Min:  77.103, Avg:  86.781, Max: 102.083 [Mbps]
--- fwrite summary: Size:  256 [KB], Min:  79.247, Avg:  86.442, Max: 104.692 [Mbps]
--- fwrite summary: Size:  512 [KB], Min:  80.168, Avg:  88.355, Max: 105.281 [Mbps]
--- fwrite summary: Size: 1024 [KB], Min:  80.636, Avg:  88.837, Max: 100.904 [Mbps]
---  fread summary: Size:    1 [KB], Min:  42.667, Avg:  58.183, Max:  64.001 [Mbps]
---  fread summary: Size:    2 [KB], Min:  73.144, Avg:  80.002, Max:  85.335 [Mbps]
---  fread summary: Size:    4 [KB], Min: 102.402, Avg: 102.402, Max: 102.402 [Mbps]
---  fread summary: Size:    8 [KB], Min: 113.780, Avg: 119.768, Max: 120.473 [Mbps]
---  fread summary: Size:   16 [KB], Min: 120.473, Avg: 123.749, Max: 124.124 [Mbps]
---  fread summary: Size:   32 [KB], Min: 120.473, Avg: 121.007, Max: 122.271 [Mbps]
---  fread summary: Size:   64 [KB], Min: 124.124, Avg: 125.743, Max: 126.033 [Mbps]
---  fread summary: Size:  128 [KB], Min: 128.504, Avg: 128.504, Max: 128.504 [Mbps]
---  fread summary: Size:  256 [KB], Min: 128.757, Avg: 128.969, Max: 129.010 [Mbps]
---  fread summary: Size:  512 [KB], Min: 130.944, Avg: 131.048, Max: 131.074 [Mbps]
---  fread summary: Size: 1024 [KB], Min: 130.748, Avg: 130.789, Max: 130.813 [Mbps]
nsh> rm -r /mnt/emmc/temp

ファイルサイズを 1 ~ 1024 KB まで増やしながら、複数回 fwrite を実行したときのファイル書き込み速度(単位:Mbps) を最小値、平均値、最大値の順番に fwrite summary の行に表示します。続けて、複数回 fread を実行したときファイル読み込み速度(単位:Mbps)を fread summary の行に表示します。

fsperf は次のコマンドオプションをもっています。

nsh> fsperf
Usage: fsperf [-i] [-n <num>] -f <file>
FileSystem Performance Monitor:
  -i: Display the information of each result
  -n: Specify the repeat count, default is 10
  -f: Specify the path to prefix of example files
      e.g.
      "-f /mnt/spif/test" on SPI-Flash
      "-f /mnt/sd0/test"  on SD card
      "-f /mnt/emmc/test" on eMMC board
-i

個々の実行結果を表示したいときに指定してください。

-n

ファイルの読み書きを繰り返し行う回数を指定します。デフォルトは10回です。

-f

本サンプルで使用するファイルパスのプレフィックスを指定します。 SPI-Flash, SD カード, eMMC など、測定対象のデバイスがマウントされているパスを指定してください。

-i オプションと -n の繰り返し回数を3回にしたときの表示例は以下の通りです。 summary 行の前に個別の実行結果3回分が表示されてます。

nsh> fsperf -f /mnt/sd0/temp/test -i -n 3
File access speed monitor!!
    1 [KB] /   1.129 [ms], speed=   6.919 [Mbps]
    1 [KB] /   1.068 [ms], speed=   7.314 [Mbps]
    1 [KB] /   1.099 [ms], speed=   7.111 [Mbps]
--- fwrite summary: Size:    1 [KB], Min:   6.919, Avg:   7.111, Max:   7.314 [Mbps]
    2 [KB] /   1.068 [ms], speed=  14.629 [Mbps]
    2 [KB] /   1.038 [ms], speed=  15.059 [Mbps]
    2 [KB] /   1.038 [ms], speed=  15.059 [Mbps]
--- fwrite summary: Size:    2 [KB], Min:  14.629, Avg:  14.913, Max:  15.059 [Mbps]
    :

fsperf の実行結果は、SD カードの種類や使用状況によって結果が異なります。 手持ちの SD カードでパフォーマンスを測定してみてください。

13. HostIF チュートリアル

13.1. HostIF サンプルアプリケーション

この章では、HostIF サンプルアプリケーションの動作手順を示します。

本サンプルアプリケーションは、対向Host機器にも Spresense ボードを使用し、 Spresense 同士を接続して HostIF 通信を実現するアプリケーションになります。 サンプルプログラム内には、Host側の実装コードも含まれています。

13.1.1. 動作環境

I2C 接続構成

B2Bコネクタピッチ変換基板のI2C3端子と、Host環境としてメインボードのI2C0端子とを接続します。
I/O電圧は1.8Vです。

tutorial hostif i2c
B2Bコネクタピッチ変換基板 Host

No.

端子名

端子名

I/O電圧

77

I2C3_BCK

I2C0_SCL

1.8V

71

I2C3_BDT

I2C0_SDA

1.8V

SPI 接続構成

B2Bコネクタピッチ変換基板のSPI2端子と、Host環境としてメインボードのSPI5端子とを接続します。
I/O電圧は1.8Vです。

tutorial hostif spi
B2Bコネクタピッチ変換基板 Host

No.

端子名

端子名

I/O電圧

77

SPI2_CS_X

SPI5_CS_X

1.8V

75

SPI2_MOSI

SPI5_MOSI

1.8V

73

SPI2_MISO

SPI5_MISO

1.8V

71

SPI2_SCK

SPI5_SCK

1.8V

13.1.2. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    I2CかSPIのどちらか接続構成に合わせて、適切な方を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/hostif_i2c (I2C接続時)
    tools/config.py examples/hostif_spi (SPI接続時)
    make
    
  3. nuttx.spk を 両方のSpresense ボードへ書き込みます。

    この例では、シリアルポートとして、HostIF側は /dev/ttyUSB0、Host側は /dev/ttyUSB1 に接続されているものとします。 シリアルポートの番号はユーザ環境に依存して変わります。 書き込み速度の baudrate として 500000 bps を設定しています。 書き込みに失敗する場合は、baudrate を 115200 bps へ変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    tools/flash.sh -c /dev/ttyUSB1 -b 500000 nuttx.spk
    

13.1.3. 動作確認

HostIF側、Host側それぞれのシリアルターミナルを開きます。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして、HostIF側は /dev/ttyUSB0、Host側は /dev/ttyUSB1 に接続されているものとします。 シリアルポートはユーザ環境に依存して変わります。baudrate はどちらも 115200 bps を設定してください。

    minicom -D /dev/ttyUSB0 -b 115200
    minicom -D /dev/ttyUSB1 -b 115200
    
  2. HostIF側のNuttShell から hostif コマンドを実行します。

    HostIFドライバを初期化した後に、
    HostIF通信用バッファにBuild Versionを書き込みます。
    HostIF通信用バッファに周期的にタイムスタンプを書き込むスレッドを起動します。
    Hostから送られてきたデータをループバックして送り返すスレッドを起動します。

    nsh> hostif
    Start updater: update the information periodically
    version: 0.0.0-SDK2.2.0-81ac8ad May 12 2021 15:53
    Start loopback: loopback the received data
  3. Host側のNuttShell から host_i2c もしくは host_spi コマンドを実行します。

    I2CまたはSPI通信により、HostIF側のBuild Versionを読み出して表示します。
    Hostからデータを送信し、HostIF側でループバックされて戻ってきたデータを受信し表示します。
    最後に、HostIF側のタイムスタンプ情報を読み出して表示します。

    nsh> host_i2c
    version=0.0.0-SDK2.2.0-81ac8ad May 12 2021 15:53 (sz=41)
    Send done.
     00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
    Send done.
     01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10
    Send done.
     02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11
    Send done.
     03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12
    Send done.
     04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13
    Send done.
     05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14
    Send done.
     06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15
    Send done.
     07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16
    Send done.
     08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
    Send done.
     09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18
    sec=15 nsec=85478117 (sz=8)
    sec=16 nsec=95518210 (sz=8)
    sec=17 nsec=105558303 (sz=8)
    sec=18 nsec=115598396 (sz=8)
    sec=19 nsec=125638489 (sz=8)
    sec=20 nsec=135678582 (sz=8)
    sec=21 nsec=145718675 (sz=8)
    sec=22 nsec=155758768 (sz=8)
    sec=23 nsec=165798861 (sz=8)
    sec=24 nsec=175838954 (sz=8)

13.1.4. サンプルコード解説

サンプルコードにおける通信バッファの構成や通信内容について解説します。

HostIFの詳細仕様については、SDK開発ガイドを参照してください。

13.1.4.1. 通信バッファ構成

HostからSpresenseへ送信する通信バッファを1個(Index=0)と、SpresenseからHostへ送信する通信バッファを3個(Index=1,2,3)を作成します。

Diagram

各通信バッファの用途は以下の通りです。

Index Size Direction Device filename Description

0

0x100

Read

/dev/hostifr0

ループバック受信用(汎用通信用途)

1

0x100

Write

/dev/hostifw1

ループバック送信用(汎用通信用途)

2

0x029

Write

/dev/hostifw2

バージョン固定値送信用(固定データ用途)

3

0x008

Write

/dev/hostifw3

タイムスタンプ送信用(リアルタイム更新データ用途)

バッファ構成のサンプルコードを以下に示します。

#ifdef CONFIG_EXAMPLES_HOSTIF_I2C
static struct hostif_i2cconf_s conf =
#else
static struct hostif_spiconf_s conf =
#endif
{
#ifdef CONFIG_EXAMPLES_HOSTIF_I2C
  .address = 0x24, /* own slave address */
#endif
  .buff[0] =
    {
      /* BUFFER0: receive buffer from host */

      0x100,
      HOSTIF_BUFF_ATTR_READ  | HOSTIF_BUFF_ATTR_VARLEN
    },

  .buff[1] =
    {
      /* BUFFER1: send buffer to host */

      0x100,
      HOSTIF_BUFF_ATTR_WRITE | HOSTIF_BUFF_ATTR_VARLEN
    },

  .buff[2] =
    {
      /* BUFFER2: send the constant version information */

      VERSION_NAMELEN,
      HOSTIF_BUFF_ATTR_WRITE | HOSTIF_BUFF_ATTR_FIXLEN
    },

  .buff[3] =
    {
      /* BUFFER3: send the variable timestamp information */

      sizeof(struct timespec),
      HOSTIF_BUFF_ATTR_WRITE | HOSTIF_BUFF_ATTR_FIXLEN
    },
};
13.1.4.2. HostIF 動作

HostIF側のサンプルコードは以下を参照してください。

サンプルコードの処理の流れを以下に示します。

  1. 指定したバッファ構成で HostIF ドライバを初期化します。

    #ifdef CONFIG_EXAMPLES_HOSTIF_I2C
      ret = hostif_i2cinitialize(&conf);
    #else
      ret = hostif_spiinitialize(&conf);
    #endif
  2. hostif_updater スレッドを起動します。

    Buffer(Index=2)へ uname() で得られるビルドバージョンを書き込みます。 これはHostIFが一度だけ情報を書き込み、HostからはRead-onlyな固定値データとして参照するような用途を想定しています。

      /* Write-once the constant software version to BUFFER2 */
    
      struct utsname name;
    
      uname(&name);
    
      ret = write(wfd2, &name.version, VERSION_NAMELEN);

    Buffer(Index=3)へ clock_gettime() で得られるタイムスタンプ情報を1秒周期で書き込みます。 これはHostIF側が定期的に情報を上書き更新し、Hostからは非同期に読み出しを行うことで常に最新のデータを取得するような用途を想定しています。

      struct timespec ts;
    
      clock_gettime(CLOCK_REALTIME, &ts);
    
      ret = write(wfd3, &ts, sizeof(ts));
  3. hostif_loopback スレッドを起動します。

    Host から送信されたデータを Buffer(Index=0) で受信し、ループバックして Buffer(Index=1) 経由でHostに送信します。

      /* blocking read */
    
      size = read(rfd, buffer, sizeof(buffer));
    
      /* blocking write */
    
      size = write(wfd, buffer, size);
13.1.4.3. Host 動作

Host側のサンプルコードは以下を参照してください。

サンプルコードの処理の流れを以下に示します。

  1. I2C または SPI ドライバを open します。

      /* Open the i2c driver */
    
      fd = open("/dev/i2c0", O_WRONLY);
      /* Open the spi driver */
    
      fd = open("/dev/spi5", O_WRONLY);
  2. Buffer(Index=2) からバージョン情報を読み出します。

      /* Get the version information from slave */
    
      get_version(fd);

    HostからはRead-onlyな固定値情報として常に参照することができるようにするために、 通信プロトコル上、バッファロックフラグを立てた状態で受信操作を行います。 バッファロック状態をキープすることで、HostIF側からの更新が禁止され常にHostから読み出し可能な状態になります。

  3. Buffer(Index=0)へデータを送信し、ループバックされたデータをBuffer(Index=1)から受信します。

    この動作を10回繰り返します。

      /* Loopback */
    
      for (i = 0; i < 10; i++)
        {
          /* Send incremental data to slave */
    
          send_data(fd);
    
          /* Wait a moment */
    
          usleep(10 * 1000);
    
          /* Receive looped-back data */
    
          receive_data(fd);
        }
  4. Buffer(Index=3) からタイムスタンプ情報を読み出します。

    1秒周期でこの動作を10回繰り返します。

      for (i = 0; i < 10; i++)
        {
          /* Get the timestamp from slave */
    
          get_timestamp(fd);
    
          /* Wait a second until the timestamp is updated */
    
          sleep(1);
        }
  5. 最後にI2C または SPI ドライバを close します。

      /* Close the i2c or spi driver */
    
      close(fd);

14. FW Update チュートリアル

14.1. FW Update サンプルアプリケーション

この章では、ファームウェアアップデートを行うサンプルアプリケーションの動作手順を示します。

アップデート対象となる各種ファームウェア(SPKファイル)を結合した package.bin パッケージファイルを作成し、 SPI-FlashやSD Cardなどのストレージ上に置いておきます。アプリケーションからそのパッケージファイルを指定して、ファームウェアをアップデートすることができます。

14.1.1. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/fwupdate を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/fwupdate
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

14.1.2. FW Update パッケージの作成

examples/fwupdate ディレクトリ以下にある package.sh スクリプトを用いて、アップデート対象のファームウェアを結合したパッケージファイル package.bin を作成します。

nuttx.spk, loader.espk, gnssfw.espk をまとめて一度にアップデートするときは package.sh スクリプトの引数に複数のファイルを指定します。スクリプトの実行に成功すると、カレントディレクトリに package.bin ファイルを作成します。

cd spresense/sdk
../examples/fwupdate/package.sh nuttx.spk ../firmware/spresense/{loader,gnssfw}.espk
Pack: 0 181392 nuttx.spk
Pack: 1 129968 ../proprietary/spresense/bin/loader.espk
Pack: 1 454512 ../proprietary/spresense/bin/gnssfw.espk
====================
Created package.bin
====================

次のようにして、nuttx.spk 単独のパッケージファイルを作成することも可能です。

../examples/fwupdate/package.sh nuttx.spk
Pack: 0 332512 nuttx.spk
====================
Created package.bin
====================

作成した package.bin ファイルを SPI-Flash 上に転送します。

./tools/flash.sh -c /dev/ttyUSB0 -w package.bin
xmodem
>>> Install files ...
nsh> xmodem /mnt/spif/package.bin
Install package.bin
|0%-----------------------------50%------------------------------100%|
######################################################################

nsh>
Transfer completed.

この例では、USBシリアル経由で package.bin をSPI-Flashのアプリケーション領域に転送しています。
応用例として、WiFiやLTEなどネットワーク経由で package.bin を転送したり、package.bin の配置場所としてSD Cardを使用することもできます。

14.1.3. 動作確認

シリアルターミナルを開いて、fwupdate コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から fwupdate コマンドを実行します。

    fwupdateコマンドのUsageを以下に示します。

    nsh> fwupdate
    FW Update Example!!
    Free space 1343488 bytes
    
    Usage: fwupdate [-f <filename>]... [-p <pkgname>] [-h]
    
    Description:
     FW Update operation
    Options:
     -f <filename>: update a file.
     -p <pkgname> : update a package.
     -z : update a package via USB CDC/ACM Zmodem.
     -h: Show this message

    Free space にファームウェア格納領域の空き容量が表示されます。

    ファームウェアアップデートは冗長化のために十分な空き容量が必要になります。 目安として、fwupdateで指定するパッケージファイルのサイズはこの空き容量の半分以下になるようにしてください。

    SPI-Flash上の package.bin を指定してファームウェアアップデートを行います。

    nsh> fwupdate -p /mnt/spif/package.bin
    FW Update Example!!
    Free space 2449408 bytes
    File: /mnt/spif/package.bin
    Size: 765896
    File: /mnt/spif/package.bin(0)
    Size: 181392
    Type: FW_APP
    ->dl(0x2d03caf0, 65536 / 181392): ret=0
    ->dl(0x2d03caf0, 131072 / 181392): ret=0
    ->dl(0x2d03caf0, 181392 / 181392): ret=0
    File: /mnt/spif/package.bin(1)
    Size: 129968
    Type: FW_SYS
    ->dl(0x2d03caf0, 65536 / 129968): ret=0
    ->dl(0x2d03caf0, 129968 / 129968): ret=0
    File: /mnt/spif/package.bin(2)
    Size: 454512
    Type: FW_SYS
    ->dl(0x2d03caf0, 65536 / 454512): ret=0
    ->dl(0x2d03caf0, 131072 / 454512): ret=0
    ->dl(0x2d03caf0, 196608 / 454512): ret=0
    ->dl(0x2d03caf0, 262144 / 454512): ret=0
    ->dl(0x2d03caf0, 327680 / 454512): ret=0
    ->dl(0x2d03caf0, 393216 / 454512): ret=0
    ->dl(0x2d03caf0, 454512 / 454512): ret=0
    Package validation is OK.
    Saving package to "gnssfw"
    Package validation is OK.
    Saving package to "loader"
    Package validation is OK.
    Saving package to "nuttx"
    
    NuttShell (NSH) NuttX-10.1.0
    nsh>

    ファームウェアアップデートに成功すると、自動的に再起動されて新しいファームウェアで起動します。

    ファームウェアアップデート中にリセットや電源断などが発生しないように注意してください。 空き領域不足やリセットなどにより万が一再起動中にエラーが発生したときはリカバリ処理が走り、 パッケージ内の全ファームウェア(この例では、gnssfw, loader, nuttx)のアップデートがキャンセルされ、 アップデート実行前の旧ファームウェアで起動します。

15. ELTRES チュートリアル

15.1. ELTRES サンプルアプリケーション

ELTRES SDK を Spresense 向けにポーティングしたソースコードが外部ライブラリに追加されています。

ELTRES SDK についての詳細は、ELTRES ホームページをご参照ください。
ユーザ登録を行うことで各種データシートやサンプルコードを入手することができます。

ELTRES サンプルアプリケーションとして次の3つのアプリケーションを用意しています。

また、ELTRES ボードは、以下の2種類に対応しています。
どちらのボードを使用するかは、SDKコンフィグレーションによって切り替えることができます。

15.1.1. eltres_lpwa サンプルアプリケーション

ELTRESホームページからダウンロード可能な LPWA Sample Application を Spresense 向けにポーティングしたサンプルコードです。

本サンプルは、予めモジュールに設定された通信プロファイルにしたがって、周期的に ELTRES 送信を行います。 また、送信周期間隔が一定時間以上空いた場合、省電力化のため CXM150x の電源を OFF する制御を行います。

ELTRESの送信にはGPSから取得した正確な時刻が必要になるため、GPSを測位可能な環境で動作させてください。

15.1.1.1. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/eltres_lpwa を追加してコンフィグレーションを実行します。

    tools/config.py examples/eltres_lpwa
    

    デフォルトはSPEXELボード用になっています。Add-onボードで使用する場合は、menuconfigでボードの選択を切り替えてください。

    make menuconfig
    Application Configuration
      -> Spresense SDK
        -> Externals
          -> ELTRES SDK library
            -> ELTRES board selection => Add-on board
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
15.1.1.2. 動作確認

シリアルターミナルを開いて、eltres_lpwa コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から eltres_lpwa コマンドを実行します。

    GPSから時刻情報を取得した後に、通信プロファイルにしたがって周期的に送信動作を行います。

    nsh> eltres_lpwa
    main_LPWA_sample_app:Ver.3.0.3, API:Ver.3.0.4
    *** pow_enable_remain_offset:340
    *** periodic1_eanable:1  periodic2_eanable:0
    *** periodic  enable:1
    *** event     enable:0
    -wait GNSS ready-
    sys_stt_event_callback:code=1(FETCHING_TIME)
    sys_stt_event_callback:code=3(EPM_FILL)
    -GNSS ready-
    local time:11:26.59(utc)
    RTC init:2023-03-02 11:26:59
    +++++time check start+++++
    current time:11:26.59(utc) next time:11:29.42(utc)
    next interval:-177 sec
    CXM150x power not change
    +++++time check end  +++++
    *** push button to event send ***
    int1_callback
    *** normal send start! ***
    sys_stt_event_callback:code=9(DF_WAIT_TX_START)
    GGA: time[112931.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112932.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112933.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112934.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112935.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112936.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112937.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112938.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112939.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    GGA: time[112940.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    sys_stt_event_callback:code=10(DF_TX_PROGRESS)
    *** START LPWA TX: Stop get GNSS! ***
    GGA: time[112941.00] lat[N 3518.xxxx] lng[E 13934.xxxx] cs_correct[1]
    sys_stt_event_callback:code=3(EPM_FILL)
    +++++time check start+++++
    current time:11:29.57(utc) next time:11:32.41(utc)
    next interval:-176 sec
    CXM150x power not change
    +++++time check end  +++++
    *** normal send end! ***

15.1.2. eltres_standalone サンプルアプリケーション

ELTRESホームページからダウンロード可能な Standalone Mode Sample Application を Spresense 向けにポーティングしたサンプルコードです。

スタンドアローン機能は、電源制御を CXM150x 自身が行い、自動ペイロード生成機能を利用したモードになります。

本サンプルは、通信制御および電源制御、および基本的なペイロードのデータ生成は CXM150x の EEPROM 設定に従って CXM150x 自身が行い、アプリケーションコードでは CXM150x の起動時の動作モード設定とペイロードの一部変更のみを行います。ペイロードへのユーザーデータ付加の例として、CXM150x が自動生成したペイロードの末尾部分をボードのtick(動作時間)に置き換えるサンプルとなっています。

ELTRESの送信にはGPSから取得した正確な時刻が必要になるため、GPSを測位可能な環境で動作させてください。

スタンドアローンモードで動作させる場合は、EEPROM の値を正しく設定する必要があります。詳細は、ELTRESホームページ上のConfiguration Manualやアプリケーションノートを参照してください。
CXM1501GR チップはスタンドアローンモードに非対応なので、Add-on ボードでは動作しません。
15.1.2.1. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/eltres_standalone を追加してコンフィグレーションを実行します。

    tools/config.py examples/eltres_standalone
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
15.1.2.2. 動作確認

シリアルターミナルを開いて、eltres_standalone コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から eltres_standalone コマンドを実行します。

    GPSから時刻情報を取得した後に、通信プロファイルにしたがって周期的に送信動作を行います。

    nsh> eltres_standalone
    main_standalone_mode_sample_app:Ver.1.0.3
    evt:| SYS RESET POR_PIN
    snd:< SYS MODE SET 00
    rcv:> SYS MODE SET OK
    response OK
    host resume:
    evt:| TX PLD xxxxxxxxxxxxxxxxxxxxxxxxxxxxx3FF
    snd:< TX PLD SET xxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyy
    rcv:> TX PLD SET OK
    response OK
    host resume:
    evt:| TX PLD xxxxxxxxxxxxxxxxxxxxxxxxxxxxx3FF
    snd:< TX PLD SET xxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzz
    rcv:> TX PLD SET OK
    response OK

15.1.3. eltres_eeprom サンプルアプリケーション

本サンプルは、ELTRESモジュール内のEEPROMを読み書きするためのユーティリティツールです。

EEPROM値の詳細は、ELTRESホームページ上のConfiguration Manualを参照してください。

ELTRESボード内のEEPROMには、予め通信プロファイルにしたがった設定値が書き込まれて出荷されています。誤って書き換えた場合に正しく通信できなくなる恐れがあるため、書き換える際は十分に留意して行ってください。
15.1.3.1. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/eltres_eeprom を追加してコンフィグレーションを実行します。

    tools/config.py examples/eltres_eeprom
    

    デフォルトはSPEXELボード用になっています。Add-onボードで使用する場合は、menuconfigでボードの選択を切り替えてください。

    make menuconfig
    Application Configuration
      -> Spresense SDK
        -> Externals
          -> ELTRES SDK library
            -> ELTRES board selection => Add-on board
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
15.1.3.2. 動作確認

シリアルターミナルを開いて、eltres_eeprom コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から eltres_eeprom コマンドを実行します。

    Usage は以下の通りです。

    nsh> eltres_eeprom
    Usage: eltres_eeprom command
     dump                  : Dump all EEPROM data
     get <address>         : Read data from an EEPROM address
     put <address> <value> : Write any value to an EEPROM address
     setlist               : Write EEPROM data pre-set by the user
     version               : Show version information

    サブコマンドにversionを指定すると、バージョン情報を取得することができます。

    nsh> eltres_eeprom version
    Bootloader : FY0100 / Apr  9 2021 16:54:39
    Firmware : 002000243233393412473931 / RA2400 / 877F5FC1 / Apr  9 2021 16:56:48
    GNSS FW : 17166,3dac91c,122 / 0x00151d31 / 0x00850401
    API version : 3.0.4

    サブコマンドにdumpを指定すると、LfourIDとEEPROM値をダンプ表示することができます。

    nsh> eltres_eeprom dump
    LfourID = 0001018350
    Name, Address, Value(HEX), Value(DEC)
    DEVICE ID UPPER, 0x0000, 0x8004610, 134235664
    DEVICE ID_LOWER, 0x0004, 0x1EB2DB3, 32189875
    LFOUR ID UPPER, 0x0008, 0x0, 0
    LFOUR ID LOWER, 0x000C, 0x1018350, 16876368
    MODULE_SELECT, 0x0020, 0x1, 1
    MODULE_BRANCH, 0x0028, 0x1, 1
    SM_TOUT, 0x0204, 0x0, 0
    :

    EEPROMの特定アドレスの値を取得するときは、サブコマンドにget、引数にアドレスを指定してください。

    EEPROMの特定アドレスの値を設定するときは、サブコマンドにput、引数にアドレスと値を指定してください。

    サブコマンドのsetlistを使ってEEPROM値をまとめて書き換えることも可能です。 eltres_eeprom_main.c の s_userlist[] 配列の中身を書き換えて、設定したいアドレスとデータのペアを設定した後にビルドし直して使用してください。

    static struct eeprom_addr_value_s s_userlist[] =
    {
      { 1, 1 },
      { 2, 2 },
    };

16. その他のチュートリアル

16.1. json サンプルアプリケーション

この章では、NuttXの標準サンプルである、jsonに関するサンプルアプリケーションの動作手順を示します。 このサンプルは、組み込み向けオープンソースの、 cJSONをライブラリとして利用しています。

cJSONはjsonフォーマットで記載されたテキストをパースするためのライブラリです。+ json形式のファイルはネットなどでのやり取りする情報などの構造化によく用いられています。

ビルド時にcJSONをwgetにてダウンロードされるため、wgetをインストールしておく必要があります。

16.1.1. ソースコードパス

このサンプルのソースコードは、 sdk/apps/examples/json 以下にあります。
また、ダウンロードされるオープンソースcJSONは、 sdk/apps/netutils/cJSON 以下にダウンロードされます。

16.1.2. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/json を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/json
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

16.1.3. 動作確認

シリアルターミナルを開いて、json コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から json コマンドを実行します。

    このサンプルコマンドでは、cJSONを用いて、サンプルコード内にハードコードされたjson形式のテキストデータのパースや、 json形式のテキストの生成を行います。

    nsh> json
    {
      "name": "Jack (\"Bee\") Nimble",
      "format": {
        "type": "rect",
        "width":  1920,
        "height": 1080,
        "interlace":  false,
        "frame rate": 24
      }
    }
    ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
    [[0, -1, 0], [1, 0, 0], [0, 0, 1]]
    {
      "Image":  {
        "Width":  800,
        "Height": 600,
        "Title":  "View from 15th Floor",
        "Thumbnail":  {
          "Url":  "http:/*www.example.com/image/481989943",
          "Height": 125,
          "Width":  "100"
        },
        "IDs":  [116, 943, 234, 38793]
      }
    }
    [{
        "precision":  "zip",
        "Latitude": 37.7668,
        "Longitude":  -122.3959,
        "Address":  "",
        "City": "SAN FRANCISCO",
        "State":  "CA",
        "Zip":  "94107",
        "Country":  "US"
      }, {
        "precision":  "zip",
        "Latitude": 37.371991,
        "Longitude":  -122.02602,
        "Address":  "",
        "City": "SUNNYVALE",
        "State":  "CA",
        "Zip":  "94085",
        "Country":  "US"
      }]
    {
      "name": "Jack (\"Bee\") Nimble",
      "format": {
        "type": "rect",
        "width":  1920,
        "height": 1080,
        "interlace":  false,
        "frame rate": 24
      }
    }
    ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
    [[0, -1, 0], [1, 0, 0], [0, 0, 1]]
    {
      "Image":  {
        "Width":  800,
        "Height": 600,
        "Title":  "View from 15th Floor",
        "Thumbnail":  {
          "Url":  "http:/*www.example.com/image/481989943",
          "Height": 125,
          "Width":  "100"
        },
        "IDs":  [116, 943, 234, 38793]
      }
    }
    [{
        "precision":  "zip",
        "Latitude": 37.7668,
        "Longitude":  -122.3959,
        "Address":  "",
        "City": "SAN FRANCISCO",
        "State":  "CA",
        "Zip":  "94107",
        "Country":  "US"
      }, {
        "precision":  "zip",
        "Latitude": 37.371991,
        "Longitude":  -122.026,
        "Address":  "",
        "City": "SUNNYVALE",
        "State":  "CA",
        "Zip":  "94085",
        "Country":  "US"
      }]

16.2. ini_dumper サンプルアプリケーション

この章では、NuttXの標準サンプルである、ini_dumperに関するサンプルアプリケーションの動作手順を示します。 このサンプルは、組み込み向けオープンソースのiniファイルパーサー、 inihを利用しています。

iniファイルはWindowsで設定ファイルとしてよく用いられているフォーマットです。+ アプリ開発を行う際に、複数の機材に対して同じアプリを使い、それぞれの機材に合わせて変更したい場合などに便利です。

ビルド時にinihをwgetにてダウンロードされるため、wgetをインストールしておく必要があります。

16.2.1. ソースコードパス

このサンプルのソースコードは、 sdk/apps/examples/ini_dumper 以下にあります。
また、ダウンロードされるオープンソースinihは、 sdk/apps/fsutils/inih 以下にダウンロードされます。

16.2.2. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/ini_dumper を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/ini_dumper
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

16.2.3. 動作確認

シリアルターミナルを開いて、ini_dumper コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から ini_dumper コマンドを実行します。

    このサンプルコマンドは、引数にiniファイルのパスを指定することで、そのファイルをパースして表示してくれます。
    このチュートリアルでは、以下のコマンドで、まずは、/mnt/spif/sample.iniを作成し、それをパースしてみます。

    nsh> echo [System] > /mnt/spif/sample.ini
    nsh> echo Board = Spresense >> /mnt/spif/sample.ini
    nsh> echo  >> /mnt/spif/sample.ini
    nsh> echo [Information] >> /mnt/spif/sample.ini
    nsh> echo ProvidedBy = Sony Semiconductor Solutions >> /mnt/spif/sample.ini

    上記コマンドで/mnt/spifにsample.iniが作られているかcatコマンドで確認します。 無事にファイルが作られていれば、以下の様なメッセージが出力されます。

    nsh> cat /mnt/spif/sample.ini
    [System]
    Board = Spresense
    
    [Information]
    ProvidedBy = Sony Semiconductor Solutions

    ファイルが作られていることが確認できたら、サンプルコマンドを使ってそのファイルをパースします。

    nsh> ini_dumper /mnt/spif/sample.ini
    
    ------ --------------- ------------- ---------------------------
     line      section          key                 value
    ------ --------------- ------------- ---------------------------
     2      System          Board         Spresense
     5      Information     ProvidedBy    Sony Semiconductor Solutions
    ------ --------------- ------------- ---------------------------
    ini_parse() exited with 0

16.3. pdcurses サンプルアプリケーション

この章では、NuttXの標準サンプルである、pdcursesに関するサンプルアプリケーションの動作手順を示します。 このサンプルは、オープンソースのPDCursesをNuttXにポーティングされた PDCursesをNuttXにポーティングされたものを利用しています。

このサンプルには、
charset, newdemo, testcurs, worm, firework, rain, tui, xmas
という8つのデモが含まれています。

16.3.1. ソースコードパス

このサンプルのソースコードは、 sdk/apps/examples/pdcurses 以下にあります。
また、PDCursesのポーティングされたソースコードは、 sdk/apps/graphics/pdcurs34 以下にあります。

16.3.2. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/pdcurses を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/pdcurses
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

16.3.3. 動作環境

このサンプルを動作させるには、以下のハードウェアを使う前提となっています。

Spresense Main Board
Spresense Extension Board
Arduino UNO LCD Connector board
ILI9341 2.2inch LCD

16.3.4. 動作確認

このサンプルには、PDCursesを使った複数のサンプルデモコマンドが用意されています。 シリアルターミナルを開いて、各コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
16.3.4.1. NuttShell から 各サンプルのコマンドを実行します。
  1. charset の実行

    charset サンプルコマンドは、PDCursesで扱う文字をLCD画面中央に表示し終了します。

    nsh> charset
  2. newdemo の実行

    newdemo サンプルコマンドは、PDCursesを使って様々なテキストウィンドウを表示したり、 文字を動かしたりします。このコマンドは一度実行すると終了せずに動きっぱなしになります。 別のサンプルコマンドを実行したい場合は、Spresemseメインボードのリセットボタンを押して再起動してください。

    nsh> newdemo
  3. worm の実行

    worm サンプルコマンドは、赤、青、緑のワーム(ミミズの様なもの)がLCD上をうねうねします。 このコマンドは一度実行すると終了せずに動きっぱなしになります。 別のサンプルコマンドを実行したい場合は、Spresemseメインボードのリセットボタンを押して再起動してください。

    nsh> worm
  4. firework の実行

    firework サンプルコマンドは、PDCursesを用いて花火の様なグラフィックを表示します。 このコマンドは一度実行すると終了せずに動きっぱなしになります。 別のサンプルコマンドを実行したい場合は、Spresemseメインボードのリセットボタンを押して再起動してください。

    nsh> firework
  5. rain の実行

    rain サンプルコマンドは、PDCursesを用いて雨の雫が落ちた様なグラフィックを表示します。 このコマンドは一度実行すると終了せずに動きっぱなしになります。 別のサンプルコマンドを実行したい場合は、Spresemseメインボードのリセットボタンを押して再起動してください。

    nsh> rain
  6. tui の実行

    tui サンプルコマンドは、PDCursesを用いてテキストベースのユーザインターフェイスを表示します。 このコマンドは一度実行すると終了せずに動きっぱなしになります。 別のサンプルコマンドを実行したい場合は、Spresemseメインボードのリセットボタンを押して再起動してください。

    nsh> tui
  7. xmas の実行

    xmas サンプルコマンドは、PDCursesを用いクリスマスっぽいアニメーションを表示します。

    nsh> xmas

16.4. embedlog サンプルアプリケーション

この章では、NuttXの標準サンプルである、embedlogに関するサンプルアプリケーションの動作手順を示します。 このサンプルは、組み込み向けの非常に軽いオープンソースのログライブラリ、 embedlogを利用しています。

ビルド時にembedlogをwgetにてダウンロードされるため、wgetをインストールしておく必要があります。

16.4.1. ソースコードパス

このサンプルのソースコードは、 sdk/apps/examples/embedlog 以下にあります。
また、ダウンロードされるオープンソースembedlogは、 sdk/apps/system/embedlog 以下にダウンロードされます。

16.4.2. ビルド手順

CLI 版を使ったビルド手順について書かれていますが、IDE 版でも同様のコンフィグレーションを選択することにより本サンプルアプリケーションをビルドすることができます。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、コンフィグレーションツールのTab補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. コンフィグレーションとビルドを行います。

    引数に examples/embedlog を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make distclean
    tools/config.py examples/embedlog
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate として 500000 bps を設定しています。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

16.4.3. 動作確認

シリアルターミナルを開いて、embedlog コマンドを実行します。

  1. シリアルターミナルを起動します。

    以下は、minicom ターミナルを使用する例です。 シリアルポートとして /dev/ttyUSB0 を、baudrate として 115200 bps を設定しています。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から embedlog コマンドを実行します。

    実行すると、以下の様に、サンプルコードの内容にしたがって、様々なフォーマットでのログ出力を確認することができます。

    nsh> embedlog
    i/Right after init, embedlog will print to stderr with just log level information - these are default settings.
    We can disable information about log level
    message still will be filtered by log level
    but there is no way to tell what level message is
    [24] As every respected logger, we also have timestamps
    [24] which work well with time from time()
    [8] or CLOCK_MONOTONIC from POSIX
    [1970-01-01 00:00:24] we also have long format that works well with time()
    [1970-01-01 00:00:24] if higher precision is needed we can use CLOCK_REALTIME
    [24] we can also mix REALTIME with short format
    [24] and if you don't need high resolution
    [24] you can disable fractions of seconds to save space!
    [24.878] or enable only millisecond resolution
    [24.879011] or enable only microsecond resolution
    [24.879255804] or enable only nanosecond resolution (not that it makes much sense on nuttx)
    no time information, if your heart desire it
    [embedlog_main.c:134] log location is very useful for debugging
    [1970-01-01 00:00:24.880201831][embedlog_main.c:139] f/Different scenarios need different options
    [1970-01-01 00:00:24.880690103][embedlog_main.c:140] a/So we can mix options however we want
    [1970-01-01 00:00:24.888532972][embedlog_main.c:143] f/you can also remove printing new line to join el_oprint and el_oputs in a singld
    [1970-01-01 00:00:24.901472180][embedlog_main.c:150] d/And if we have
    [1970-01-01 00:00:24.909162464][embedlog_main.c:151] i/modern terminal
    [1970-01-01 00:00:24.916822231][embedlog_main.c:152] n/we can enable colors
    [1970-01-01 00:00:24.924543032][embedlog_main.c:153] w/to spot warnings
    [1970-01-01 00:00:24.929822473][embedlog_main.c:154] e/or errors
    [1970-01-01 00:00:24.937543274][embedlog_main.c:155] c/with a quick
    [1970-01-01 00:00:24.945203041][embedlog_main.c:156] a/glance into
    [1970-01-01 00:00:24.950482482][embedlog_main.c:157] f/log file
    [1970-01-01 00:00:24.958142249][embedlog_main.c:160] i/embedlog: you can also use prefixes
    [1970-01-01 00:00:24.965954601][embedlog_main.c:161] i/embedlog: to every message you send
    [1970-01-01 00:00:24.973766953][embedlog_main.c:164] i/set prefix to null to disable it
    [1970-01-01 00:00:24.984020665][embedlog_main.c:196] i/print whole ASCII table
    [1970-01-01 00:00:24.991802500][embedlog_main.c:197] i/0x0000  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f  ................
    [1970-01-01 00:00:25.002319292][embedlog_main.c:197] i/0x0010  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f  ................
    [1970-01-01 00:00:25.015258500][embedlog_main.c:197] i/0x0020  20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f   !"#$%&'()*+,-./
    [1970-01-01 00:00:25.028228225][embedlog_main.c:197] i/0x0030  30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?       [1970-01-01 00:00:25.038695556][embedlog_main.c:197] i/0x0040  40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO       [1970-01-01 00:00:25.051604247][embedlog_main.c:197] i/0x0050  50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f  PQRSTUVWXYZ[\]^_
    [1970-01-01 00:00:25.064573972][embedlog_main.c:197] i/0x0060  60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f  `abcdefghijklmno
    [1970-01-01 00:00:25.075041303][embedlog_main.c:197] i/0x0070  70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f  pqrstuvwxyz{|}~.
    [1970-01-01 00:00:25.087888960][embedlog_main.c:199] i/print memory region that contains string with NULL chars
    [1970-01-01 00:00:25.098356291][embedlog_main.c:201] i/0x0000  73 6f 6d 65 20 6d 65 73 73 61 67 65 00 74 68 61  some message.tha
    [1970-01-01 00:00:25.111264982][embedlog_main.c:201] i/0x0010  74 20 63 6f 6e 74 61 69 6e 73 00 6e 75 6c 6c 20  t contains.null
    [1970-01-01 00:00:25.121701796][embedlog_main.c:201] i/0x0020  63 68 61 72 61 63 74 65 72 73 00                 characters.
    [1970-01-01 00:00:25.134457902][embedlog_main.c:203] i/print the same region but this time with nice ascii table
    [1970-01-01 00:00:25.144772648][embedlog_main.c:205] i/------  -----------------------------------------------  ----------------
    [1970-01-01 00:00:25.157620305][embedlog_main.c:205] i/offset  hex                                              ascii
    [1970-01-01 00:00:25.167935051][embedlog_main.c:205] i/------  -----------------------------------------------  ----------------
    [1970-01-01 00:00:25.180874259][embedlog_main.c:205] i/0x0000  73 6f 6d 65 20 6d 65 73 73 61 67 65 00 74 68 61  some message.tha
    [1970-01-01 00:00:25.191341590][embedlog_main.c:205] i/0x0010  74 20 63 6f 6e 74 61 69 6e 73 00 6e 75 6c 6c 20  t contains.null
    [1970-01-01 00:00:25.204250281][embedlog_main.c:205] i/0x0020  63 68 61 72 61 63 74 65 72 73 00                 characters.
    [1970-01-01 00:00:25.214595544][embedlog_main.c:205] i/------  -----------------------------------------------  ----------------

17. System tools 一覧

Spresense SDKでは、NuttShell から 各種 System tools を利用することができます。

カテゴリ System tool名 説明

LOG

setlogmask

ログレベルを動的に変更します。

logdump

BackupSRAMに保存されたロギング情報をダンプします。

logsave

BackupSRAMに保存されたロギング情報をSPI-Flashに保存します。

GPIO

gpio

Pin情報を取得/設定します。

gpioint

GPIO割り込みを確認するためのテストコマンドです。

I2C

i2c

I2Cデバイスとの通信を確認するためのユーティリティツールです。

PMIC

pmic

PMIC(PowerManagement IC)を制御するためのユーティリティツールです。

USB

usbmsc

USB MSC機能を使用するためのコマンドです。

cdcacm

USB CDC/ACM機能を使用するためのコマンドです。

Zmodem

zmodem

Zmodem転送を使用するためのコマンドです。

Stack

stackmonitor

Stack使用量をモニタするためのツールです。

Network

lte_sysctl

LTEのネットワークデーモンを開始/停止するためのコマンドです。

18. GPIO ユーティリティツール

GPIO ユーティリティツールを利用して、各ピンの状態を確認したり、GPIO ピンへの入出力を設定することができます。

18.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/gpiotool を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py feature/gpiotool
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

18.2. 動作確認

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から gpio コマンドを実行します。コマンドオプションを以下に示します。

    nsh> gpio -h
    USAGE: gpio command
     stat [<from_pin>] [<end_pin>]
     conf <pin> [-m <0|1|2|3>] [-i] [-H] [-p <0|1|2|3>]
      -m: function mode
      -i: input enable
      -H: Higher drive current/slew rate
      -p: 0=float, 1=pullup, 2=pulldown, 3=buskeeper
     read <pin>
     write <pin> <0|1|-1>

    gpio stat コマンドにより、指定したピン番号の設定値を表示します。
    gpio conf コマンドにより、指定したピン番号の設定を変更することができます。
    gpio read コマンドにより、指定したピン番号の入力値を読み出します。
    gpio write コマンドにより、指定したピン番号へ出力値を書き込みます。

18.2.1. gpio stat コマンド

gpio stat コマンドにより、デジタルピン一覧の状態を表示します。
引数で表示範囲を指定することもできます。

nsh> gpio stat 97 100
-------------------------------------------------------------
( No)PIN NAME          : Mode I/O mA Pull Read IRQ Type NF EN
-------------------------------------------------------------
( 97)PIN_I2S1_BCK      : 0     /  2  --   0    -1
( 98)PIN_I2S1_LRCK     : 0     /  2  --   0    -1
( 99)PIN_I2S1_DATA_IN  : 0     /  2  --   0    -1
(100)PIN_I2S1_DATA_OUT : 0     /  2  --   0    -1
Mode

ピンの機能モードを表します。
Mode 0 は GPIO 機能、Mode 1~3 は I2C や UART といった GPIO 以外の機能・用途で使用されます。
各ピンはいくつかのグループに分けられており、Mode はグループ単位で設定されます。
ピンの詳細は、開発ガイド Pin specification を参照してください。

I/O

Input はピンの入力が許可されているかどうかを表します。
Output は GPIO モードに設定されたピンの出力が有効かどうかを表します。

mA

ドライブ電流 (2mA or 4mA) を表します。

Pull

--:Float, PU:Pull-Up, PD:Pull-Down, BK:Bus-Keeper を表します。

Read

読み出した値を表示します。

IRQ

GPIO 割り込み機能が設定されている場合、IRQ 番号を表します。

Type

GPIO 割り込み機能が設定されている場合、極性を表します。
(High:レベル, Low:レベル, Rise:立ち上がりエッジ, Fall:立ち下がりエッジ, Both:両エッジ)

NF

GPIO 割り込み機能が設定されている場合、チャタリング防止用のノイズフィルタの有効/無効を表します。

EN

GPIO 割り込み機能が設定されている場合、割り込みの許可/禁止を表します。

18.2.2. gpio conf コマンド

gpio conf コマンドは、指定したピン番号に対して、機能モードや入出力設定を変更することができます。
例えば PIN_SEN_IRQ_IN(37番) ピンを GPIO モードの入力ピン、Pull-Downに設定する場合、gpio conf 37 -m 0 -i -p 2 と入力します。

nsh> gpio conf 37 -m 0 -i -p 2
nsh> gpio stat 37
-------------------------------------------------------------
( No)PIN NAME          : Mode I/O mA Pull Read IRQ Type NF EN
-------------------------------------------------------------
( 37)PIN_SEN_IRQ_IN    : 0    I/  2  PD   0    -1

18.2.3. gpio read コマンド

gpio read コマンドにより、指定したピン番号への入力値を読み出します。
PIN_SEN_IRQ_IN (37番) ピンを読み出す場合は gpio read 37 と入力し、結果が 0 (LOW), 1 (HIGH) で表示されます。

nsh> gpio read 37
0
nsh> gpio read 37
1

18.2.4. gpio write コマンド

gpio write コマンドにより、指定したピン番号に 0 (LOW), 1 (HIGH), -1 (HiZ) を設定します。

メインボードの LED に接続された PIN_I2S1_BCK(97番) ピンに対して gpio write 97 1 と入力すると LED0 が点灯、 gpio write 97 0 と入力すると LED0 が消灯します。

nsh> gpio write 97 1
nsh> gpio write 97 0

18.3. プログラミング

18.3.1. GPIO API

GPIO の API の詳細については APIリファレンスマニュアル を参照してください。

GPIO API を使用する場合は、以下のファイルを include してください。

#include <arch/board/board.h>
#include <arch/chip/pin.h>

GPIO API のプロトタイプ宣言はこちらに定義されています
nuttx/boards/arm/cxd56xx/spresense/include/cxd56_gpioif.h

GPIO API で使用するピン名はこちらに定義されています
nuttx/arch/arm/include/cxd56xx/pin.h

18.3.2. GPIO 入力設定

GPIO 入力ピンとして設定してポートの読み出しを行う場合は次のように行います。

  /* 入力ピン設定 */

  board_gpio_config(PIN_XXX, 0, true, false, PIN_FLOAT);     (1)
  board_gpio_config(PIN_XXX, 0, true, false, PIN_PULLUP);    (2)
  board_gpio_config(PIN_XXX, 0, true, false, PIN_PULLDOWN);  (3)
  board_gpio_config(PIN_XXX, 0, true, false, PIN_BUSKEEPER); (4)

  /* ポート入力 */

  int status = board_gpio_read(PIN_XXX);                     (5)
1 PIN_XXX の入力を許可します。
2 PIN_XXX をPull-Upに設定し入力を許可します。
3 PIN_XXX をPull-Downに設定し入力を許可します。
4 PIN_XXX をBus-Keeperに設定し入力を許可します。
5 PIN_XXX の入力値を読み出します。

18.3.3. GPIO 出力設定

GPIO 出力ピンとして設定してポートの書き込みを行う場合は次のように行います。

  /* 出力ピン設定 */

  board_gpio_config(PIN_XXX, 0, false, true,  PIN_FLOAT); (1)
  board_gpio_config(PIN_XXX, 0, false, false, PIN_FLOAT); (2)

  /* ポート出力 */

  board_gpio_write(PIN_XXX, 0);   (3)
  board_gpio_write(PIN_XXX, 1);   (4)
  board_gpio_write(PIN_XXX, -1);  (5)
1 PIN_XXX の入力を禁止し、ドライブ電流を4mAに設定します。
2 PIN_XXX の入力を禁止し、ドライブ電流を2mAに設定します。
3 PIN_XXX へ LOW を出力します。
4 PIN_XXX へ HIGH を出力します。
5 PIN_XXX の出力を禁止にします。

18.3.4. GPIO 割り込み設定

GPIO 割り込みを使用する場合は次のように行います。

static int gpio_handler(int irq, FAR void *context, FAR void *arg)
{
  /* 割り込みハンドラ */
}

  /* 割り込み設定 */

  board_gpio_intconfig(PIN_XXX, INT_HIGH_LEVEL,  false, gpio_handler); (1)
  board_gpio_intconfig(PIN_XXX, INT_LOW_LEVEL,   false, gpio_handler); (2)
  board_gpio_intconfig(PIN_XXX, INT_RISING_EDGE,  true, gpio_handler); (3)
  board_gpio_intconfig(PIN_XXX, INT_FALLING_EDGE, true, gpio_handler); (4)
  board_gpio_intconfig(PIN_XXX, INT_BOTH_EDGE,    true, gpio_handler); (5)

  board_gpio_int(PIN_XXX, false); (6)
  board_gpio_int(PIN_XXX, true);  (7)
1 PIN_XXX を HIGH レベル割り込みを設定し、割り込みハンドラを登録します。
2 PIN_XXX を LOW レベル割り込みを設定、割り込みハンドラを登録します。
3 PIN_XXX を立ち上がりエッジ割り込みを設定し、割り込みハンドラを登録します。ノイズフィルタを有効にしています。
4 PIN_XXX を立ち下がりエッジ割り込みを設定し、割り込みハンドラを登録します。ノイズフィルタを有効にしています。
5 PIN_XXX を両エッジ割り込みを設定し、割り込みハンドラを登録します。ノイズフィルタを有効にしています。
6 PIN_XXX の割り込みを禁止します。
7 PIN_XXX の割り込みを許可します。

19. PMIC ユーティリティツール

PMIC ユーティリティツールを利用して、PMIC(CXD5247) に接続されたロードスイッチやGPO出力を設定することができます。

19.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/pmictool を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py feature/pmictool
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

19.2. 動作確認

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から pmic コマンドを実行します。コマンドオプションを以下に示します。

    nsh> pmic -h
    
    Usage: pmic [-h] [-l] [-e <target>] [-d <target>]
                [-r <addr>] [-w <addr> -v <value>]
    
    Description:
     PMIC utility tool
    Options:
     -l: Show power status of the target
     -e <target>: Enable power to the target
     -d <target>: Disable power to the target
     -z <target>: Set GPO to HiZ to the target
     -r <addr>: Single read from <addr>
     -w <addr> -v <value>: Single write <value> to <addr>
     -h: Show this message

    pmic -l オプションにより、一覧を表示します。

    nsh> pmic -l
         Target Name : on/off
         ----------- : ------
              DDC_IO : on
            LDO_EMMC : on
             DDC_ANA : off
             LDO_ANA : on
            DDC_CORE : on
            LDO_PERI : off
                LSW2 : off
                LSW3 : on
                LSW4 : on
                GPO0 : hiz
                GPO1 : off
                GPO2 : off
                GPO3 : off
                GPO4 : off
                GPO5 : off
                GPO6 : off
                GPO7 : off

    pmic -e GPO0 と入力すると GPO0 を Enable に設定します。
    pmic -d GPO0 と入力すると GPO0 を Disable に設定します。
    pmic -z GPO0 と入力すると GPO0 を Hi-Z に設定します。

19.3. 接続先

ロードスイッチ(LSW) や GPO は主に各種電源コントロールに使用されています。
メインボードや拡張ボードでの接続先についての一覧を表示します。

DDC_CORE, DDC_IO, LDO_ANA についてはそれぞれプロセッサの CORE 電源, IO 電源, アナログ電源に接続されているため、Disable しないでください。 また、LDO_PERI についてはボード上、未接続のため記載を省略しています。
Target 出力電圧 メインボード 拡張ボード LTE 拡張ボード

LDO_EMMC

3.3V

ソケット3.3V出力
(GNSS外部アンテナ用)

-

-

LSW2

1.8V

Audio DVDD

-

-

LSW3

SPI-Flash

-

-

LSW4

TCXO 26MHz
GNSS用LNA

-

-

GPO0

VSYS(4.0V)

-

-

3.3V LDO

GPO1

-

Audio 3.3V LDO

GPO2

-

-

LTE DCDC

GPO3

-

-

(LTE VBAT)

GPO4

Camera LDO

-

-

GPO5

-

-

-

GPO6

-

Audio Headphone Amp.

GPO7

-

-

-

19.4. プログラミング

19.4.1. PMIC API

PMIC 制御 API を使用する場合は、以下のファイルを include してください。

#include <arch/board/board.h>

PMIC API のプロトタイプ宣言はこちらに定義されています
nuttx/boards/arm/cxd56xx/spresense/include/cxd56_power.h

PMIC API で使用するターゲット名はボード依存ファイルの中で POWER_LTE のようなエイリアス名が定義されています
nuttx/boards/arm/cxd56xx/spresense/include/board.h

19.4.2. GPO 出力設定

GPO 出力ピンに書き込みを行う場合は次のように行います。
ON/OFF する場合は、board_power_control(), board_power_control_tristate() のどちらを使用しても構いません。
HiZ へ設定する場合は、board_power_control_tristate() 関数を使用してください。

  /* GPOポート出力 */

  board_power_control(PMIC_GPO(0), true);  (1)
  board_power_control(PMIC_GPO(0), false); (2)

  board_power_control_tristate(PMIC_GPO(0), 1);  (3)
  board_power_control_tristate(PMIC_GPO(0), 0);  (4)
  board_power_control_tristate(PMIC_GPO(0), -1); (5)
1 GPO0 を Enable します。
2 GPO0 を Disable します。
3 GPO0 を Enable します。
4 GPO0 を Disable します。
5 GPO0 を HiZ へ設定します。

19.4.3. GPO 出力モニタ

GPO 出力ピンの状態を読み出す場合は次のように行います。
ON/OFF を読み出す場合は、board_power_monitor(), board_power_monitor_tristate() のどちらを使用しても構いません。
HiZ 状態も読み出す場合は、board_power_monitor_tristate() 関数を使用してください。

  /* GPOポート出力モニタ */

  bool bstate = board_power_monitor(PMIC_GPO(0)); (1)

  int istate = board_power_monitor_tristate(PMIC_GPO(0)); (2)
1 GPO0 の設定値を読み出します。true のときは Enable、false のときは Disable です。
2 GPO0 の設定値を読み出します。1 のときは Enable、0 のときは Disable、-1 のときは Hi-Z です。

20. USB MSC 機能を使う

USB MSC (Mass Storage Class) 機能を使うためのユーティリティコマンドについて説明します。
USB MSC 機能を有効にするとホスト PC からSpresense ボード上の SD カードへ直接アクセスすることができます。

20.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/usbmsc を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py feature/usbmsc
    make
    

    SD カードを USB MSC としてマウントするので SD カード機能も有効化しています。

  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

20.2. 動作確認

拡張ボードに SD カードが挿入されている状態で、拡張ボードの USB 端子とホスト PC とを USB ケーブルで接続します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. USB MSC 機能を開始する場合、NuttShell から msconn コマンドを実行します。

    nsh> msconn
    mcsonn_main: Creating block drivers
    mcsonn_main: Configuring with NLUNS=1
    mcsonn_main: handle=d038d50
    mcsonn_main: Bind LUN=0 to /dev/mmcsd0
    mcsonn_main: Connected

    ホスト PC に新たなリムーバブルディスクが認識され、 ホスト PC から拡張ボードの SD カードの内容にアクセスすることができます。

  3. USB MSC 機能を終了する場合、NuttShell から msdis コマンドを実行します。

    nsh> msdis
    msdis: Disconnected

21. USB CDC/ACM 機能を使う

USB CDC/ACM 機能を使うためのユーティリティコマンドについて説明します。
USB CDC/ACM 機能を有効にすると拡張ボードの USB をシリアルポートとして使用することができます。

21.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/usbcdcacm を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py feature/usbcdcacm
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

21.2. 動作確認

拡張ボードの USB 端子とホスト PC とを USB ケーブルで接続します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. USB CDC/ACM 機能を開始する場合、NuttShell から sercon コマンドを実行します。

    nsh> sercon
    sercon: Registering CDC/ACM serial driver
    sercon: Successfully registered the CDC/ACM serial driver

    Spresense ボード上に /dev/ttyACM0 が追加され、ホスト PC には新たな COM ポートが見つかります。 そのポートを使って、USB 経由でシリアル通信をすることができるようになります。

  3. USB CDC/ACM 機能を終了する場合、NuttShell から serdis コマンドを実行します。

    nsh> serdis
    serdis: Disconnected

22. Zmodem を使ったファイル転送

この章では、Zmodem 転送を利用して HostPC と Spresense ボードとの間でファイルを送受信する方法について示します。

22.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/zmodem を指定してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py feature/zmodem
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

22.2. 動作手順

Zmodem 転送機能に対応したシリアルターミナルを使用してください。

ここでは、minicom を例にとって説明します。
minicom や lrzsz がインストールされていない場合は事前にインストールしてください。

sudo apt install minicom lrzsz

minicom を起動します。

minicom -D /dev/ttyUSB0 -b 115200

NuttShell から Zmodem の rz (受信), sz (送信) コマンドを使用することができます。

rz コマンドの使い方
tutorial zmodem1 rz
sz コマンドの使い方
tutorial zmodem1 sz

22.2.1. HostPC から Spresense ボードへのファイル転送

HostPC から Spresense ボードへファイルを転送する手順を以下に示します。

  1. minicom 上で CTRL-a を押下した後に z キーを押してメニューを開きます。(このショートカットキーの割り当てはユーザー側で変更可能です。詳細は minicom のマニュアルを参照してください。)
    続けて s キーを押して Send files (ファイル送信) を選択します。

    tutorial zmodem2 menu
  2. カーソルキーで zmodem を選択して Enter キーで実行します。

    tutorial zmodem2 upload
  3. カーソルキーとスペースキーでフォルダを移動をして転送したいファイルを選択します。
    カーソルキーでフォルダを選びスペースキーを2回押すとフォルダへ移動できます。
    カーソルキーでファイルを選びスペースキーで選択した後に Enter キーを押すと転送を開始します。

    tutorial zmodem2 dir

    もしくは、Enter キーを押してファイル名を入力して転送を実行することもできます。

    tutorial zmodem2 file
  4. ファイル転送が始まり、Transfer complete と表示されれば転送完了です。

    tutorial zmodem2 complete
  5. Spresense ボード上のファイルの転送先は、CONFIG_SYSTEM_ZMODEM_MOUNTPOINT で変更することができます。
    デフォルトのコンフィギュレーションでは、Flash 上の /mnt/spif 以下にファイルが転送されます。

TeraTerm を使用する場合は、TeraTerm メニューから ファイル → 転送 → ZMODEM → 送信 から選択したファイルを送信することができます。

22.2.2. Spresense ボードから HostPC へのファイル転送

Spresense ボードから HostPC へファイルを転送する手順を以下に示します。

  1. NuttShell 上で、sz コマンドの引数に転送したいファイルを指定して実行します。
    ファイル名は / から始まるフルパス名を入力してください。
    以下の例は、-x 1 バイナリ転送オプションを付けています。

    nsh> sz -x 1 /mnt/spif/test00.dat
  2. ファイル転送が始まり、Transfer complete と表示されれば転送完了です。

    tutorial zmodem3 complete
  3. HostPC 上の minicom を実行したフォルダにファイルが転送されます。

TeraTerm を使用する場合は、NuttShell から sz コマンドを入力した後に、ファイル → 転送 → ZMODEM → 受信 によりファイルを受信することができます。HostPC 側のファイル受信ディレクトリは、ファイル → ディレクトリを変更 によって指定可能です。

23. アプリケーションの自動起動方法

この章では、電源投入時に NuttShell プロンプトではなくユーザーアプリケーションを自動的に起動する方法について記述します。 NuttShellでは、簡単なシェルスクリプトを実行することが出来、かつ、特定のスクリプトを起動時に実行する機能を持っています。 この章では、その機能を用いた、自動起動について解説します。

NuttShell 上で利用できる基本的なコマンドや、if, whileといった制御構文については、 NuttShell (NSH) documentation を参照してください。

23.1. Spresense SDK でのスタートアップスクリプトの使い方

NuttX で自動起動スクリプトを利用するには、コンフィグでその機能を有効にする必要があります。 Spresense SDK では、自動起動を有効にするためのデフォルトとなるコンフィグを提供しています。

この章での例としては、"hello" ビルドインコマンドを自動起動スクリプトを使って、 起動後、3秒おきにhelloを表示させるスクリプトを作成してみます。

23.1.1. コンフィグレーション

まずは、今回の例で使用する "hello" ビルドインコマンドと、自動起動を有効にするデフォルトコンフィグを使って、 SDKのコンフィグを行います。
spresense/sdk ディレクトリに移動して、以下のようにconfig.pyに2つのパラメータを指定してを実行してください。

./tools/config.py examples/hello feature/startup_script

一つ目のパラメータ examples/hello は "hello" ビルドインコマンドを有効にするオプションで、2つ目のパラメータ feature/startup_script は、自動起動の仕組みを有効にするオプションになります。

このように、Spresense SDK の config.py では、オプションに色々と追加していくことでSDKとして用意しているデフォルトコンフィグを追加していく、ということが出来るようになっています。

23.1.2. ビルドとフラッシュ

コンフィグが完了したら、同じ spresense/sdk ディレクトリ下で、以下のコマンドを実行して nuttx.spk を実機に書き込みます。

make
tools/flash.sh -c /dev/ttyUSB0 nuttx.spk
>>> Install files ...
install -b 115200
Install nuttx.spk
|0%-----------------------------50%------------------------------100%|
######################################################################

xxxxx bytes loaded.
Package validation is OK.
Saving package to "nuttx"
updater# sync
updater# Restarting the board ...
reboot

23.1.3. 動作確認(自動起動なし)

nuttx.spk を書き込んだだけでは、自動的に起動することはありません。 ここでは、まずは書き込んだ nuttx.spk が正しく "hello" ビルドインコマンドを含んでいるか、 確認してみます。 まず、適当なターミナルソフト(以下の例は minicom を使用しています)を使って、実機のUARTポートに接続します。この例では、実機のポートは、 /dev/ttyUSB0 としていますが、お使いの環境に合わせて設定してください。

minicom -D /dev/ttyUSB0 -b 115200

実機に接続すると、以下のメッセージが出て、 nsh> プロンプトが表示されます。

No /mnt/spif/init.rc.

NuttShell (NSH) NuttX-8.2
nsh>
ターミナルによっては、接続時にボードにリセットをかけないものもあるようです。上記のメッセージはリセット直後に表示されるものなので、表示されないような場合は、ターミナルは接続したまま、Spresenseボードのリセットを押してみてください。

この状態で "help" と叩くと以下のように Builtin Apps: の中に hello があることが確認できます。

nsh> help
help usage:  help [-v] [<cmd>]

  [          dirname    free       mb         mv         set        unset
  ?          date       help       mkdir      mw         sh         usleep
  basename   dd         hexdump    mkfatfs    poweroff   sleep      xd
  break      df         ifconfig   mkfifo     ps         test
  cat        echo       ifdown     mkrd       pwd        time
  cd         exec       ifup       mksmartfs  reboot     true
  cp         exit       kill       mh         rm         uname
  cmp        false      ls         mount      rmdir      umount

Builtin Apps:
  hello  nsh
nsh>

ここで、 hello を実行して、動作を確認します。

nsh> hello
Hello, World!!
nsh>

これで、 hello が動作することは確認できました。 次に、これを3秒おきに実行するスクリプトを作成して、実機で動作させます。

23.1.4. 動作確認(自動起動)

前の節で hello の動作を確認したら、一度ターミナルから抜け、スクリプトファイルを作成します。

スクリプトファイル名は、 init.rc としてください。 以下のようなスクリプトを作成します。

  1. init.rc

    while true
    do
      sleep 3
      hello
    done

シンプルなスクリプトですが、 while true で無限ループを作り、 sleep 3 で3秒待ち、 hello を実行する、というのを延々と繰り返すスクリプトになります。

このスクリプトをSpresenseメインボードのSPIフラッシュに書き込みます。 init.rc が spresense/sdk ディレクトリ内にあるとして、spresense/sdk ディレクトリ内で、以下のようなコマンドを実行してください。

./tools/flash.sh -w init.rc
xmodem
>>> Install files ...
nsh> xmodem /mnt/spif/init.rc
Install init.rc
|0%-----------------------------50%------------------------------100%|
######################################################################

nsh>

flash.sh は、 -w オプションを指摘することで、その後の引数で指定されたファイルを、実機のSPIフラッシュに書き込みを行います。

flash.shを -w で実行すると、nuttx.spk を再度書き込む必要が出てきます。 すでにビルド済みの nuttx.spk が spresense/sdk にあるので、それを再度書き込みます。

tools/flash.sh -c /dev/ttyUSB0 nuttx.spk
>>> Install files ...
install -b 115200
Install nuttx.spk
|0%-----------------------------50%------------------------------100%|
######################################################################

xxxxx bytes loaded.
Package validation is OK.
Saving package to "nuttx"
updater# sync
updater# Restarting the board ...
reboot

この状態で再度ターミナルに接続してみます。

Run /mnt/spif/init.rc.
sh [8:100]

NuttShell (NSH) NuttX-8.2
nsh> Hello, World!!
Hello, World!!
Hello, World!!
Hello, World!!
Hello, World!!

繋ぐと、3秒おきに、意図した通り、 "Hello, World!!" というメッセージが表示されることが確認できます。

23.2. TIPS: Application エントリーポイント

スタートアップスクリプトとは別に CONFIG_INIT_ENTRYPOINT を変更するという方法もあります。

デフォルトのコンフィギュレーションでは、CONFIG_INIT_ENTRYPOINT=spresense_main となっています。

これを例えば hello_main に変更すると、起動時に hello アプリケーションを起動するように変更できます。

tutorial autostart entrypointv200
エントリーポイントの関数名は、サンプルコードのMakefileで定義している PROGNAME 変数に入っている文字列に、_main を付けたものになります。例:spresense/sdk/apps/examples/hello/Makefileに、PROGNAMEの定義が $(CONFIG_EXAMPLES_HELLO_PROGNAME) となっています。これは、Kconfigのパラメータで名前が確定しており、spresense/sdk/apps/examples/Kconfigを見ると、config EXAMPLES_HELLO_PROGNAME の欄に、default "hello" と記載されています。この場合、PROGNAMEがhelloとなるため、 "hello_main"がエントリー関数名となります。
起動時にアプリケーションの初期化処理が必要です。以下を参考に、#include <sys/boardctl.h> を追加し、ユーザーエントリーポイント関数の中から boardctl(BOARDIOC_INIT, 0); を呼び出してください。
#include <sys/boardctl.h>

int main(int argc, FAR char *argv[])
{
  /* Initialize apllication */
  boardctl(BOARDIOC_INIT, 0);

  printf("Hello, World!!\n");
  return 0;
}

24. ローダブルELFチュートリアル

ローダブルELFとは、OSとアプリケーションを別々のバイナリで作成し、動作時にアプリケーションをロードして実行できる機能です。

アプリケーションをローダブルELFとして作成すると、必要に応じてメモリにロードすることが可能になるため、実行時の総メモリ量を抑えたり、アプリケーションを単独で更新することができるようになります。ただし、アプリケーションの起動時間が伸びたり、ロード/アンロードを繰り返す事によるメモリの断片化が発生する可能性もあるため注意が必要です。

ASMP ELFとの互換性はありません。起動するアプリケーションはメインコアでのみ実行されます。

このチュートリアルでは、アプリケーションのELFファイルの格納場所にSDカードを使用します。

24.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/loadable device/sdcard を指定してコンフィグレーションを実行します。

    tools/config.py feature/loadable device/sdcard
    tools/config.py -m
    

    メニューコンフィグを開き、Hello, World! example を有効にします。この時、Hello, World! exampleM となるように設定するとそのアプリケーションがローダブルELFとしてビルドされます。

    [Application Configuration]
      [Examples]
        ["Hello, World!" example] => M

    コンフィグレーションが完了したらビルドします。

    make
    

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが、sdk/apps/bin 以下に ELFファイル hello がそれぞれ生成されます。hello は別途SDカードにコピーしてください。

  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

24.2. 動作確認

シリアルターミナルを開いて、hello アプリケーションを実行します。

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から hello アプリケーションを実行します。

    SDカードにある hello ELFファイルまでのフルパスを指定します。

    nsh> /mnt/sd0/hello
    Hello, World!!

24.3. PATH変数を使用する

NuttShellでは、bashの環境変数と同様 PATH 変数を用いてELFファイルを検索するパスの設定ができます。 feature/loadable にはこれを使用するための設定が含まれていますが、実際に使用するパスはユーザー自身で設定する必要があります。 メニューコンフィグを開いて PATH 変数にSDカードのパスを設定します。この設定を行うことでSDカードの直下に置かれているアプリケーションはフルパスを指定しなくても実行することができるようになります。

[Binary Loader]
  [Initial PATH Value] => "/mnt/sd0"

ビルドして nuttx.spk を書き込んでください。起動後、NuttShellから hello とタイプするだけでSDカード上の hello アプリケーションを起動することができるようになります。

nsh> hello
Hello, World!!

25. LLVM C++ 標準ライブラリ

ユーザープログラムから LLVM C++ 標準ライブラリを利用する方法について説明します。 ビルド中に LLVM ソースコードをダウンロードしコンパイルを行うため、ネットワークに接続された環境でビルドを行ってください。

コンパイラを gcc-arm-none-eabi-9-2019-q4-major 以降のバージョンへ更新する必要があります。 バージョンが古い場合は、開発ツールのセットアップ を参考にコンパイラのバージョンアップを行ってください。

25.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/helloxx といったアプリケーションの他に feature/libcxx を追加してコンフィグレーションを実行します。 また、ここではC++テストプログラムを実行するため、-m オプションを追加して menuconfig を開きます。

    tools/config.py examples/helloxx feature/libcxx -m
    

    Application ConfigurationTestingC++ test program を有効にします。

    tutorial libcxx test

    SDKではコンパイルオプションに -fno-rtti を付けて RTTI(実行時型情報) を無効にしているため、 sdk/apps/testing/cxxtest/cxxtest_main.cxx ファイルを開いて、test_rtti() をコメントアウトしてください。

    diff --git a/testing/cxxtest/cxxtest_main.cxx b/testing/cxxtest/cxxtest_main.cxx
    index 6baa7d3..288aa40 100644
    --- a/testing/cxxtest/cxxtest_main.cxx
    +++ b/testing/cxxtest/cxxtest_main.cxx
    @@ -184,6 +184,7 @@ static void test_stl(void)
    
     static void test_rtti(void)
     {
    +#ifdef __GXX_RTTI
       std::cout << "test rtti===============================" << std::endl;
       Base *a = new Base();
       Base *b = new Extend();
    @@ -199,6 +200,7 @@ static void test_rtti(void)
    
       delete a;
       delete b;
    +#endif
     }
    
     //***************************************************************************

    make を実行し、ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

25.2. 動作確認

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から cxxtest コマンドを実行します。

    nsh> cxxtest
    test ofstream===========================
    printf: Starting test_ostream
    printf: Successfully opened /dev/console
    cout: Successfully opened /dev/console
    Writing this to /dev/console
    test iostream===========================
    Hello, this is only a test
    Print an int: 190
    Print a char: d
    test vector=============================
    v1=1 2 3
    Hello World Good Luck
    test map================================

    このテストプログラムは、iostream, vectormap を使ったシンプルなプログラム例になりますが、その他のC++標準ライブラリ(C++11規格まで)もサポートされています。 C++標準ライブラリの詳細については以下を参考にしてください。

26. SMP (Symmetric Multiprocessing)

ここではアプリケーションを SMP 環境で動かす方法や taskset コマンドの使い方について説明します。

26.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/hello といったアプリケーションの他に feature/smp を追加してコンフィグレーションを実行します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/hello feature/smp
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

    feature/smp のデフォルトはデュアルコア(2コア)構成になっています。 コア数を変更する場合は、sdk/configs/feature/smp/defconfig ファイルの SMP_NCPUS=2 の数字を変更してください。 2 ~ 最大 6 まで指定可能です。

26.2. 動作確認

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から ps コマンドを実行します。

    ps の表示結果に CPU 列が表示されこれがコア番号を表します。

    nsh> ps
      PID GROUP CPU PRI POLICY   TYPE    NPX STATE    EVENT     SIGMASK   STACK   USED  FILLED COMMAND
        0         0   0 FIFO     Kthread N-- Assigned           00000000 001024 000480  46.8%  CPU0 IDLE
        1         1   0 FIFO     Kthread N-- Running            00000000 002048 000220  10.7%  CPU1 IDLE
        2         2   0 FIFO     Kthread N-- Running            00000000 002048 000220  10.7%  CPU2 IDLE
        3         3   0 FIFO     Kthread N-- Running            00000000 002048 000120   5.8%  CPU3 IDLE
        4         4   0 FIFO     Kthread N-- Running            00000000 002048 000120   5.8%  CPU4 IDLE
        5         5   0 FIFO     Kthread N-- Running            00000000 002048 000120   5.8%  CPU5 IDLE
        7       --- 224 RR       Kthread --- Waiting  Signal    00000000 002008 000324  16.1%  hpwork
        8       --- 100 RR       Kthread --- Waiting  Signal    00000000 002008 000332  16.5%  lpwork
        9       --- 100 RR       Kthread --- Waiting  Signal    00000000 002008 000332  16.5%  lpwork
       10       --- 100 RR       Kthread --- Waiting  Signal    00000000 002008 000332  16.5%  lpwork
       12       --- 200 RR       Task    --- Waiting  MQ empty  00000000 000976 000480  49.1%  cxd56_pm_task
       13         0 100 RR       Task    --- Running            00000000 008152 001204  14.7%  init

    audio アプリケーションなど一部のアプリケーションには、コア番号が CPU=0 固定でないと動作しないという制約があります。 コア番号を指定してプログラムを実行する場合、taskset コマンドを利用することができます。

  3. NuttShell から taskset コマンドを実行します。

    taskset コマンドの使い方は以下の通りです。引数 mask には CPU 番号をビットで表したマスク値(1 << CPU番号)を指定します。

    CPU番号

    0

    1

    2

    3

    4

    5

    mask

    1

    2

    4

    8

    16

    32

    nsh> taskset -h
    taskset mask command ...
    taskset -p [mask] pid

    例えば、CPU番号=0 で動かす場合は、mask に 1 を指定してください。

    nsh> taskset 1 hello

26.3. 参考

CPU 番号を取得するための up_cpu_index() など、SMP 固有の API がいくつか用意されています。

#include <nuttx/arch.h>

  int cpu = up_cpu_index();

詳細は NuttX ドキュメントを参考にしてください。

27. Task Trace ツール

ここでは Trace Compass を用いて、タスクトレース情報をグラフィカルに表示する方法について説明します。

27.1. Trace Compass インストール

グラフィカル表示ツールとして、Trace Compass を使用します。
Trace Compass ツールのインストールについては、 NuttX Task Trace User Guide マニュアル を参照してください。

  • Trace Compass ツールをダウンロードしてインストールします。

    • 2021年8月現在の最新版は Trace Compass 7.0.0, latest release (requires Java 11) です。

    • 起動に JRE も必要になるので、別途、インストールしてください。

  • マニュアルに記載されている通り、Add-on の Trace Compass ftrace (Incubation) をインストールしてください

    • Tools → Add-ons …​ → Install Extensions Trace Compass ftrace (Incubation)

27.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に examples/camera といったアプリケーションの他に feature/tasktrace を追加してコンフィグレーションを実行します。 また、SD Card へ保存したトレースデータを拡張ボードの USB 経由で取り出すために feature/usbmsc も追加します。
    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py examples/camera feature/tasktrace feature/usbmsc
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

27.3. 動作確認

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から trace コマンドを実行します。

    trace コマンドを実行すると現在の設定情報を確認することができます。

    nsh> trace
    Task trace mode:
     Trace                   : enabled
     Overwrite               : on  (+o)
     Syscall trace           : on  (+s)
      Filtered Syscalls      : 0
     Syscall trace with args : on  (+a)
     IRQ trace               : on  (+i)
      Filtered IRQs          : 0

    trace コマンドのUsageは以下の通りです。
    詳細は NuttX Trace command description を参照してください。

    nsh> trace -h
    
    Usage: trace <subcommand>...
    Subcommand:
      start [-c][<duration>]          : Start task tracing
      stop                            : Stop task tracing
      cmd [-c] <command> [<args>...]  : Get the trace while running <command>
      dump [-c][<filename>]           : Output the trace result
      mode [{+|-}{o|s|a|i}...]        : Set task trace options
      syscall [{+|-}<syscallname>...] : Configure syscall trace filter
      irq [{+|-}<irqnum>...]          : Configure IRQ trace filter

    trace startcameratrace stop コマンドでトレースデータを取得し、trace dump 結果を SD Card に保存します。

    nsh> trace start
    nsh> camera
    nximage_listener: Connected
    nximage_initialize: Screen resolution (320,240)
    Take 10 pictures as RGB file in /mnt/sd0 after 5 seconds.
     After finishing taking pictures, this app will be finished after 10 seconds.
    Expire time is pasted. GoTo next state.
    Start captureing...
    FILENAME:/mnt/sd0/VIDEO001.RGB
    FILENAME:/mnt/sd0/VIDEO002.RGB
    FILENAME:/mnt/sd0/VIDEO003.RGB
    FILENAME:/mnt/sd0/VIDEO004.RGB
    FILENAME:/mnt/sd0/VIDEO005.RGB
    FILENAME:/mnt/sd0/VIDEO006.RGB
    FILENAME:/mnt/sd0/VIDEO007.RGB
    FILENAME:/mnt/sd0/VIDEO008.RGB
    FILENAME:/mnt/sd0/VIDEO009.RGB
    FILENAME:/mnt/sd0/VIDEO010.RGB
    Finished captureing...
    Expire time is pasted. GoTo next state.
    nsh> trace stop
    nsh> trace dump /mnt/sd0/trace.log
  3. NuttShell から msconn コマンドを実行します。

    拡張ボード側の USB を PC に接続し、msconn コマンドを実行して、PC から SD Card 内のファイルへアクセスします。

    nsh> msconn
    mcsonn_main: Creating block drivers
    mcsonn_main: Configuring with NLUNS=1
    mcsonn_main: handle=0x2d04b690
    mcsonn_main: Bind LUN=0 to /dev/mmcsd0
    mcsonn_main: Connected
  4. Trace Compass ツールからトレースデータを読み込みます。

    FileOpen Trace…​ → SD Card 上の trace.log を開きます。

    tutorial trace compass

    タスクスイッチや割り込み発生の時系列情報をグラフィカルに表示することができます。

28. デバッグログ機能について

この章では、SDK が提供するログ機能について説明します。

28.1. syslog 機能

デバッグログ出力は、システムログ (syslog) 機能を用いて行われます。syslog は、Error (エラー)、Warnings (警告)、Informational (情報) の3種類のログレベルをもち、サブシステムやドライバといったモジュールごとにログ出力を行うかどうかをコンフィグレーションにより選択することができます。

デバッグログ機能を有効にするためには、コンフィグレーション済みの状態から menuconfig を表示します。

cd spresense/sdk

tools/config.py -m
または
make menuconfig

Build SetupDebug Options から Enable Debug Features を有効にしてください。 次のメニューが表示されます。

tutorial debuglog

*** Debug SYSLOG Output Controls *** の項目で全体のログ出力レベルを決定します。 ログレベルの選択はネスト構造になっているので、例えば、Informational を有効にするときは、Error および Warnings も有効にする必要があります。

*** Debug SYSLOG Output Controls ***
[] Enable Error Output
 [] Enable Warnings Output
  [] Enable Informational Output

プログラム中のログ出力関数と CONFIG 名の関係を以下に示します。
それぞれのログ出力関数は、該当する CONFIG を有効にしたときにログが出力されるようになります。 例えば、CONFIG_DEBUG_INFO=y にしたときに _info() 関数を用いたログが出力されます。 CONFIG_DEBUG_INFO=n のように無効にしたときは、_info() はコンパイル非対象になりログは出力されません。

ログ出力関数 CONFIG名

_err()

CONFIG_DEBUG_ERROR

_warn()

CONFIG_DEBUG_WARN

_info()

CONFIG_DEBUG_INFO

NuttX共通で利用するモジュールごとのログ出力関数は、nuttx/include/debug.h に定義されています。モジュールごとにそれぞれ先頭にprefixを付けたログ出力関数が存在します。例として、ファイルシステムに関するコンフィグレーションを以下に示します。ファイルシステムに関するログ出力関数には先頭に f が付いたログ出力関数が用意されています。

[*] File System Debug Features
 [ ] File System Error Output
  [ ] File System Warnings Output
   [ ] File System Informational Output
ログ出力関数 CONFIG名

ferr()

CONFIG_DEBUG_FS_ERROR

fwarn()

CONFIG_DEBUG_FS_WARN

finfo()

CONFIG_DEBUG_FS_INFO

また、nuttx/include/debug.h に共通定義されているもの以外に、個別にデバッグログ出力をもつドライバやSDK側のモジュールにも別途コンフィグレーションが存在します。

SDK環境で使用する主なモジュールとデバッグログ機能の CONFIG 名を以下に示します。
該当モジュールに対するデバッグログを有効にする場合は、menuconfig から以下の CONFIG 名で検索してください。

モジュール CONFIG名

SCUドライバ

CONFIG_CXD56_SCU_DEBUG

modemドライバ

CONFIG_MODEM_ALT1250_DEBUG

HostIFドライバ

CONFIG_CXD56_HOSTIF_DEBUG

GNSSドライバ

CONFIG_CXD56_GNSS_DEBUG_FEATURE

ASMP

CONFIG_ASMP_DEBUG_FEATURE

Audio

CONFIG_AUDIOUTILS_{EVENT,STATE, DETAIL}LOG

mbedTLS_stub

CONFIG_LTE_NET_MBEDTLS_{ERROR,DEBUG}_MSG

MPCOMM

CONFIG_MPCOMM_DEBUG_FEATURE

Sensor Manager

CONFIG_SENSING_MANAGER_DEBUG_FEATURE

Step Counter

CONFIG_SENSING_STEPCOUNTER_DEBUG_FEATURE

xmodem

CONFIG_DEBUG_XMODEM

詳細な動作を確認したいときや個々のモジュールをデバッグしたいときなど、デバッグログ機能を有効利用してみてください。

syslog 機能と printf を併用する場合、printf 出力はバッファリングされて表示されるため、syslog ログと順序関係が前後する可能性があります。

28.2. RAM log 機能

前述した syslog は、通常、UART 等のシリアルコンソールへログを出力しますが、コンフィグレーションを変更することで 別のデバイスへ出力先を切り替えることができます。ここでは、RAM メモリ上のバッファ(以降、RAM バッファと呼ぶ)へログを出力する方法について説明します。 RAM バッファへ出力することにより、UART へアクセスするときのような速度的なオーバーヘッドを減らすことができます。 RAM バッファに溜まったログは dmesg コマンドによってまとめてコンソールに出力することが可能です。

28.2.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数にアプリケーションの他に feature/ramlog もしくは feature/ramlog_circular を追加してコンフィグレーションを実行します。 ramlog のコンフィグレーションについては、用途に応じてどちらか適切な方を選択してください。

    defconfig 説明

    feature/ramlog

    RAM バッファがフル(満杯)になるまでロギングします。フルになるとログのバッファリングが止まります。dmesg コマンドを実行するとバッファは空になり、バッファリングを再開します。

    feature/ramlog_circular

    RAM バッファがリングバッファ構造になっており、常に最新のログで上書き更新されます。

    tools/config.py feature/ramlog
    または
    tools/config.py feature/ramlog_circular
    

    もし RAM バッファのサイズを変更したいときは、menuconfig により CONFIG_RAMLOG_BUFSIZE の値を変更してください。

    make を実行し、ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

28.2.2. 動作確認

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から dmesg コマンドを実行することで、RAM バッファに溜まったタイムスタンプ付きのログをコンソールに出力します。
    以下はファイルシステムの詳細ログを有効にしたときのログ例です。

    nsh> dmesg
    [   51.161556] smart_ioctl: Entry
    [   51.161648] smart_readsector: Entry
    [   51.161709] cxd56_bread: bread: 00009000 (1 blocks)
    [   51.163510] smart_ioctl: Entry
    [   51.163571] smart_readsector: Entry
    [   51.163662] cxd56_bread: bread: 00009000 (1 blocks)
    [   51.165463] smart_ioctl: Entry
    [   51.165524] smart_readsector: Entry
    [   51.165615] cxd56_bread: bread: 00009000 (1 blocks)
    [   51.167446] smart_ioctl: Entry
    [   51.167507] smart_readsector: Entry
    [   51.167599] cxd56_bread: bread: 00009000 (1 blocks)

28.3. crash dump 機能

assertHard fault による crash が発生したときのログ(crash dump ログ)をファイルに保存する機能をサポートしています。 この機能は、feature/crashdump コンフィグレーションを使用することで利用可能です。

はじめに簡単に動作を確認するための手順を説明した後に、ソースコードの解説を行います。

28.3.1. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/crashdump を追加してコンフィグレーションを実行します。 また、crash を発生させる目的でここでは examples/watchdog も同時に指定します。

    tools/config.py feature/crashdump examples/watchdog
    

    make を実行し、ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

28.3.2. 動作確認

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell から wdog コマンドを実行すると Hard fault が発生し、その後、リブートします。

    nsh> wdog
      ping elapsed=0
      ping elapsed=496
    (snip)
    up_assert: Assertion failed at file:irq/irq_unexpectedisr.c line: 50 task: wdog
    up_registerdump: R0: 00000008 00000001 2d02e7f8 00000008 2d02e7f8 2d02e7f8 2d02e7f8 00000001
    up_registerdump: R8: 000f4240 0d026575 00000020 2d035bf4 0000001d 2d035b08 0d006601 0d006604
    (snip)
    up_stackdump: 2d035c60: 00000000 0d00421d 00000000 00000000 2d035c78 00000000 676f6477 deadbe00
    up_taskdump: Idle Task: PID=0 Stack Used=468 of 1024
    up_taskdump: hpwork: PID=1 Stack Used=324 of 2008
    up_taskdump: lpwork: PID=2 Stack Used=332 of 2008
    up_taskdump: lpwork: PID=3 Stack Used=332 of 2008
    up_taskdump: lpwork: PID=4 Stack Used=332 of 2008
    up_taskdump: cxd56_pm_task: PID=6 Stack Used=400 of 976
    up_taskdump: init: PID=7 Stack Used=944 of 8152
    up_taskdump: wdog: PID=8 Stack Used=636 of 2008
    up_taskdump: wdog: PID=8 Stack Used=636 of 2008
    
    NuttShell (NSH) NuttX-10.1.0
    nsh>
  3. リブートした後に、NuttShell から logdump crash コマンドを実行すると、Watchdog が発生したときの crash dump 情報を表示することができます。

    nsh> logdump crash
    === Dump crash at 0x04408200 (1020 bytes)
    date: Jan 01 00:00:12 1970
    file: irq/irq_unexpectedisr.c line: 50 task: wdog pid: 8
    user sp: 2d035b08
    stack base: 2d035c70
    stack size: 000007d8
    int sp: 2d027338
    stack base: 2d0273b0
    stack size: 00000800
    regs:
    S0:  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    S8:  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    S16: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    S24: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    FPSCR: 00000000
    R0:  00000008 00000001 2d02e7f8 00000008 2d02e7f8 2d02e7f8 2d02e7f8 00000001
    R8:  000f4240 0d026575 00000020 2d035bf4 0000001d 2d035b08 0d006601 0d006604
    xPSR: 61000000 BASEPRI: 000000e0 EXC_RETURN: ffffffe9
    interrupt stack:
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000 0000000d 676f6477 0d000333 00000001
    2d02e7f8 2d02e7f8 2d035a34 000000e0 0d00194d 000000e0 0d003413 0d003439
    0d00344d 00000080 0d006549 2d035bf4 00000020 0d026575 000f4240 00000000
    00000002 2d035a34 2d029a84 2d02e5b0 0d006539 0d006604 0d006601 2d035b08
    0000001d 2d035bf4 00000000 000000e0 0d008279 0d021d7c 2d035c70 2d029a84
    2d035b08 2d027338 0d00d619 04408200 04408200 044085fc 07249e03 0000000c
    0d005aab 0d021d7c 2d027338 04408200 04408200 ffffffff 0d005383 2d027308
    00000032 00000032 000003fc 2d027338 2d02e5b0 2d027338 0d011cab 000001e0
    0000001e 0d006949 00000030 0d006949 0d00fd9d 2d0273b0 0d00fd57 2d0273b0
    2d02e5b0 00000000 2d029ab8 00000080 0d00809d 2d0272d4
    user stack:
    2d035bf4 2d035bf0 0d01db85 0d026560 00000000 0d026575 000f4240 00000000
    00001b54 0d026575 2d02e7f8 ffffffff 00000000 2d035ba8 0d005897 00000000
    2d02e5b0 00000014 00000000 00000000 00000000 0d026576 00000000 00000000
    0d006977 00000000 2d035bb8 0d01dba1 0d01dbaf 00000020 2d035bb8 2d035bf4
    2002e7f8 00000020 0d01e4f5 00000000 00001b54 2d02e7f8 00000020 0d01dba1
    0d01d9e5 2d02e7f8 2d02e7f8 2d035b37 00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000 00000000 00000000 61000000 0d006604
    0d006601 0000001d 00000008 2d02e7f8 00000001 00000008 00000000 00000000
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000 00000000 00000000
    No such file: /mnt/spif/crash.log
  4. また、NuttShell から logsave コマンドを実行すると、crash dump のRAWバイナリデータを /mnt/spif/crash.log に保存することができます。 保存したバイナリファイルは、後から logdump crash コマンドを使うことでデコードして表示することができます。

    nsh> logsave
    Save at 0x04408200 (1020 bytes) into /mnt/spif/crash.log

28.3.3. 解説

NuttX では、Hard fault が発生したときに、board_crashdump() 関数が呼び出されます。 board_crashdump() 関数は、nuttx/boards/arm/cxd56xx/common/src/cxd56_crashdump.c に定義されています。

up_backuplog_alloc() 関数を呼び出し、バックアップSRAMから "crash" というキーワード名でメモリを確保しています。

#ifdef CONFIG_CXD56_BACKUPLOG
  pdump = up_backuplog_alloc("crash", sizeof(fullcontext_t));
#else

確保したバックアップSRAMメモリに対して crash dump 情報を書き込みます。

board_crashdump() 関数の最後に、board_reset_on_crash() 関数を呼び出してリブートを行います。

#if defined(CONFIG_CXD56_RESET_ON_CRASH)
  board_reset_on_crash();
#endif

ポイントとして、バックアップSRAMに保存された内容は、電源を落とさない限り保持され続けます。 crash dump はこの仕組みを利用しています。バックアップSRAMに一時的に情報を保存しておき、リブート後にその情報を参照しています。 リブート後に情報を参照することで、例えば、crash 発生時にファイルへの書き込みができないような問題を回避しています。

次に、リブート後に crash dump を取得する方法について説明します。

logdump コマンドのソースコードは sdk/system/logdump/logdump.c にあります。

コマンド引数 "crash" というキーワード名を指定して、up_backuplog_region() 関数により、crash dump が保存されているアドレスとサイズを取得します。

  /* Dump from memory */

  up_backuplog_region(name, &addr, &size);

アドレスとサイズを logdump_sub() 関数に渡して、バックアップSRAMへ保存していた crash dump 情報をデコードして表示します。

  printf("=== Dump %s at 0x%08x (%d bytes)\n", name, (uint32_t)addr, size);
  logdump_sub(name, addr, size);

crash dump のRAWバイナリデータをファイルへ保存するには logsave コマンドを利用します。logsave コマンドのソースコードは sdk/system/logsave/logsave.c にあります。

up_backuplog_entry() 関数により、バックアップSRAMに保存されたキーワード名"crash"とアドレス、サイズ情報を取得します。

  /* Get a log entry */

  ret = up_backuplog_entry(name, &addr, &size);

バックアップSRAMに保持されている情報を CONFIG_SYSTEM_LOGSAVE_MOUNTPOINT の指定先へファイルとして保存します。 CONFIG_SYSTEM_LOGSAVE_MOUNTPOINT のデフォルトは "/mnt/spif" となっており、crash dump のRAWバイナリデータは "/mnt/spif/crash.log" へ保存します。

このファイルへは追記で書き込みを行うため、起動時に毎回 logsave コマンドを発行するようにしておくことで、crash の履歴をファイルとして保存しておくことが可能です。また、ここで保存したデータは、前述した logdump コマンドによって後からデコードして表示することができます。

最後に、crash dump 以外にもバックアップSRAMに何かしら情報を保存しておき、リブート後にそれを参照するといった応用的な使い方も可能です。 ここで実装されているソースコードを参考にしてみてください。

29. コールスタックログの解析方法

この章では、Spresense上で動作させているプログラムがアサートした場合などに表示されるダンプログからコールスタックを抽出するスクリプト callstack.py の使い方について説明します。

29.1. callstack.py について

Spresenseでプログラムを実行させているとプログラムの問題などで以下のようなダンプログが表示されることがあります。

arm_hardfault: Hard Fault escalation:
arm_hardfault: PANIC!!! Hard Fault!:arm_hardfault:      IRQ: 3 regs: 0x2d035d2c
arm_hardfault:  BASEPRI: 000000e0 PRIMASK: 00000000 IPSR: 00000003 CONTROL: 00000000
arm_hardfault:  CFSR: 00040000 HFSR: 40000000 DFSR: 00000000 BFAR: e000ed38 AFSR: 00000000
arm_hardfault: Hard Fault Reason:
up_assert: Assertion failed at file:armv7-m/arm_hardfault.c line: 173 task: hello
arm_registerdump: R0: 2d0284c8 R1: 0d0258aa R2: 2d035e18  R3: 00000028
arm_registerdump: R4: 0d012481 R5: 2d0284c8 R6: 0d0258aa  FP: 2d035e18
arm_registerdump: R8: 00000028 SB: 2d02e930 SL: 2d035e18 R11: 00000000
arm_registerdump: IP: 2d02e930 SP: 2d035e00 LR: 2d035e18  PC: 00000000
arm_registerdump: xPSR: ffffffe9 BASEPRI: 000000e0 CONTROL: 00000000
arm_registerdump: EXC_RETURN: ffffffe9

...

arm_dump_stack: User Stack:
arm_dump_stack: sp:     2d035e00
arm_dump_stack:   base: 2d035688
arm_dump_stack:   size: 000007d8
arm_stackdump: 2d035e00: 2d035e18 00000028 2d02e930 2d035e18 00000000 ffffffe9 00000000 00000000
arm_stackdump: 2d035e20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d035e40: 00000000 00000000 00000000 2d035e00 000000e0 0d012481 2d0284c8 0d0258aa
arm_showtasks:    PID    PRI     STACK      USED   FILLED    COMMAND
arm_showtasks:   ----   ----      2048       456    22.2%    irq
arm_dump_task:      0      0      1000       396    39.6%    Idle Task
arm_dump_task:      1    224      1992       276    13.8%    hpwork 0x2d028720
arm_dump_task:      2    100      1992       276    13.8%    lpwork 0x2d028730
arm_dump_task:      3    100      1992       276    13.8%    lpwork 0x2d028730
arm_dump_task:      4    100      1992       276    13.8%    lpwork 0x2d028730
arm_dump_task:     13    100      2008       532    26.4%    hello
arm_dump_task:      6    200       976       464    47.5%    cxd56_pm_task
arm_dump_task:      7    100      8144      1076    13.2%    spresense_main

このログメッセージを callstack.py で解析させることによって、以下のようなコールスタックとして抽出することができます。

[0d006eb5] vsyslog + 0x15
[0d005d17] _assert + 0x7
[0d001b03] arm_hardfault + 0xa7
[0d0258aa] g_builtins + 0x76
[0d00365f] irq_dispatch + 0x17
[0d001a49] arm_doirq + 0x1d
[0d012481] hello_main + 0x1
[0d00030d] exception_common + 0x35
[0d012481] hello_main + 0x1
[0d0258aa] g_builtins + 0x76

29.2. callstack.py の使い方について

ダンプログからコールスタックを抽出するためには、以下のバイナリとテキスト情報を使います。

  • ダンプログを含むSpresenseの動作ログ(log.txt)

  • 動作させているプログラムをビルドした際に生成されたMAPファイル(System.map

以上のファイルを使って、以下のコマンドを使ってコールスタックを表示します。

$ cd sdk
$ ../nuttx/tools/callstack.py System.map log.txt

以下は examples/hello サンプルに assert 処理を意図的に入れた場合の解析結果の例になります。

$ ../nuttx/tools/callstack.py System.map log.txt
NuttShell (NSH) NuttX-11.0.0
nsh> hello
Hello, World!!
up_assert: Assertion failed at file:hello_main.c line: 40 task: hello
arm_hardfault: Hard Fault escalation:
arm_hardfault: PANIC!!! Hard Fault!:arm_hardfault:         IRQ: 3 regs: 0x2d035d2c
arm_hardfault:         BASEPRI: 000000e0 PRIMASK: 00000000 IPSR: 00000003 CONTROL: 00000000
arm_hardfault:         CFSR: 00040000 HFSR: 40000000 DFSR: 00000000 BFAR: e000ed38 AFSR: 00000000
arm_hardfault: Hard Fault Reason:
up_assert: Assertion failed at file:armv7-m/arm_hardfault.c line: 173 task: hello
arm_registerdump: R0: 2d0284c8 R1: 0d0258aa R2: 2d035e18  R3: 00000028
arm_registerdump: R4: 0d012481 R5: 2d0284c8 R6: 0d0258aa  FP: 2d035e18
arm_registerdump: R8: 00000028 SB: 2d02e930 SL: 2d035e18 R11: 00000000
arm_registerdump: IP: 2d02e930 SP: 2d035e00 LR: 2d035e18  PC: 00000000
arm_registerdump: xPSR: ffffffe9 BASEPRI: 000000e0 CONTROL: 00000000
arm_registerdump: EXC_RETURN: ffffffe9
arm_dump_stack: IRQ Stack:
arm_dump_stack: sp:     2d027a50
arm_dump_stack:   base: 2d0272c0
arm_dump_stack:   size: 00000800
arm_stackdump: 2d027a40: 00000001 00000000 00000000 0d006eb5 40000000 00000003 2d035d2c 00040000
arm_stackdump: 2d027a60: 00000028 2d02e930 2d035e18 0d005d17 00000080 0d001b03 40000000 00000000
arm_stackdump: 2d027a80: e000ed38 00000000 00000000 00000000 2d0284c8 0d0258aa 2d035e18 0d00365f
arm_stackdump: 2d027aa0: 00000000 0d001a49 00000003 00000003 2d035e00 0d012481 2d0284c8 0d00030d
arm_dump_stack: User Stack:
arm_dump_stack: sp:     2d035e00
arm_dump_stack:   base: 2d035688
arm_dump_stack:   size: 000007d8
arm_stackdump: 2d035e00: 2d035e18 00000028 2d02e930 2d035e18 00000000 ffffffe9 00000000 00000000
arm_stackdump: 2d035e20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d035e40: 00000000 00000000 00000000 2d035e00 000000e0 0d012481 2d0284c8 0d0258aa
arm_showtasks:    PID    PRI     STACK      USED   FILLED    COMMAND
arm_showtasks:   ----   ----      2048       456    22.2%    irq
arm_dump_task:      0      0      1000       396    39.6%    Idle Task
arm_dump_task:      1    224      1992       276    13.8%    hpwork 0x2d028720
arm_dump_task:      2    100      1992       276    13.8%    lpwork 0x2d028730
arm_dump_task:      3    100      1992       276    13.8%    lpwork 0x2d028730
arm_dump_task:      4    100      1992       276    13.8%    lpwork 0x2d028730
arm_dump_task:     13    100      2008       532    26.4%    hello
arm_dump_task:      6    200       976       464    47.5%    cxd56_pm_task
arm_dump_task:      7    100      8144       928    11.3%    spresense_main
----------------- callstack -----------------
[0d006eb5] vsyslog + 0x15
[0d005d17] _assert + 0x7
[0d001b03] arm_hardfault + 0xa7
[0d0258aa] g_builtins + 0x76
[0d00365f] irq_dispatch + 0x17
[0d001a49] arm_doirq + 0x1d
[0d012481] hello_main + 0x1
[0d00030d] exception_common + 0x35
[0d012481] hello_main + 0x1
[0d0258aa] g_builtins + 0x76

このログから [0d012481] hello_main + 0x1 が最後のコールスタック情報であることから、 hello_main の先頭付近でアサートしていることがわかります。

30. VTUN (Virtual Tunnels over TCP/IP networks)

VTUNはTCP/IPネットワーク上で仮想トンネルを設定できるネットワークアプリケーションです。

2つの異なるネットワークアドレス上で仮想トンネルを設定し仮想ネットワークアドレスを構築することでサーバ・クライアント間で相互Socket通信を行うことができます。

以下の構成のようにクライアントからグローバルネットワーク(aaa.bbb.ccc.0)に存在するサーバへは直接アクセスが可能でも、サーバからはLTEのローカルネットワーク(xxx.yyy.zzz.0)上に存在するSpresenseに対しては直接アクセスすることはできません。

VTUNを使い仮想ネットワーク(192.168.254.0)を構築することで、サーバからクライアントであるSpresenseに対し直接アクセスすることができるようになります。

tutorial vtun overview ja

詳しくはVTUN公式ページをご参照ください。

ここではVTUNで仮想ネットワークを構成する方法について説明します。

30.1. VTUN設定ファイルの作成

VTUNによる仮想ネットワークを構築するためには、サーバ・クライアントともに設定ファイルを用意する必要があります。

ここでは、サーバ対クライアントの1対1間の構成を例に取り設定内容について説明します。

30.1.1. サーバ側の設定ファイル

サーバ側の vtund で使用する設定ファイルです。

  • options

    ネットワーク設定時に使用する ip コマンドのパスを設定します。通常は /sbin/ip に存在するので、 /sbin/ip と設定します。

  • default

    セッション共通の設定をします。

    • type

      VTUNが使用するトンネルのタイプを指定します。

      • tun: IPトンネル

      • ether: イーサーネットトンネル

    • proto

      VTUNが使用するプロトコルを指定します。

      • udp: UDPプロトコル経由でVTUN接続

      • tcp: TCPプロトコル経由でVTUN接続

    • keepalive

      定期的な接続確認を行うかを設定します。

      • yes: 定期的に接続状態を確認する。

      • no: 定期的に接続状態を確認しない。

    • encrypt

      トンネル内でやり取りするデータの暗号化方法を指定します。

      • no: 平文のままトンネルで送受信する。

      • aes128cbc: AES暗号 128bitキー モードCBCを使って暗号化し、トンネルで送受信する。

      • aes128cfb: AES暗号 128bitキー モードCFBを使って暗号化し、トンネルで送受信する。

      • aes128ecb: AES暗号 128bitキー モードECBを使って暗号化し、トンネルで送受信する。

      • aes256cbc: AES暗号 256bitキー モードCBCを使って暗号化し、トンネルで送受信する。

      • aes256cfb: AES暗号 256bitキー モードCFBを使って暗号化し、トンネルで送受信する。

      • aes256ecb: AES暗号 256bitキー モードECBを使って暗号化し、トンネルで送受信する。

      • blowfish128ecb: Blowfish暗号 128bitキー モードECBを使って暗号化し、トンネルで送受信する。

      • blowfish256ecb: Blowfish暗号 256bitキー モードECBを使って暗号化し、トンネルで送受信する。

    • stat

      統計情報のログ保存を設定します。

      • yes: 統計カウンターをログに残す。

      • no: 統計カウンターをログに残さない。

  • session

    任意のセッション名を指定できます。以下の例では vpn を使っています。

    • passwd

      クライアント認証用のパスワードを設定します。

    • device

      サーバ上のネットワークデバイス名を指定します。

      tun の場合は tun0ether の場合は tap0 を指定します。

    • up

      ネットワークデバイスが有効化した時に実行するコマンドを記載します。

    • down

      ネットワークデバイスが無効化した時に実行するコマンドを記載します。

30.1.1.1. 設定ファイル例

以下の例では tun モードを使いサーバ・クライアント間を 192.168.254.0 のネットワークで構成します。

FILE: vtund_server.conf

options {
	ip	/sbin/ip;
}

default {
	type	tun;
	proto	tcp;
	keepalive	yes;
	encrypt	no;
	stat	yes;
}

vpn {
	passwd	spresense_vtun;
	device	tun0;

	up {
		ip "addr add 192.168.254.1/32 dev %%";
		ip "addr add 2222::2/128 dev %%";
		ip "link set mtu 1450 dev %%";
		ip "link set up dev %%";
		ip "route add default via 192.168.254.1 dev %%";
		ip "-6 route add default dev %%";
	};

	down {
		ip "link set down dev %%";
		ip "route del default";
		ip "-6 route del default";
	};
}

30.1.2. クライアント(Spresense)の設定ファイル

クライアント(Spresense)側の vtun で使用する設定ファイルです。

  • session

    任意のセッション名を指定できます。 サーバで登録されているセッション名である必要があります。

    以下の例では vpn を使っています。

    • passwd

      サーバとの認証用パスワードです。

30.1.2.1. 設定ファイル例

FILE: vtund.conf

vpn {
	passwd spresense_vtun;
}

30.2. ビルド手順

ここではコマンドラインによるビルド手順を示します。
IDE を使用してビルドする場合、以下に示すコンフィグレーション情報を参考にしてください。

  1. sdk ディレクトリへ移動します。

    build-env.sh スクリプトを読み込むことで、config.py ツールの Tab 補完機能が有効になります。

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK のコンフィグレーションとビルドを行います。

    引数に feature/lte といったネットワーク機能に feature/vtun を追加してコンフィグレーションを実行します。
    また、 以下のコンフィグを menuconfig で追加することにより wgetwebserver を有効にすることでSpresenseへの直接アクセスを確認できるアプリケーションを追加できます。

    EXAMPLES_WEBSERVER=y
    EXAMPLES_WEBSERVER_DHCPC=y
    NETUTILS_WEBSERVER=y
    NETUTILS_WEBCLIENT=y
    WEBCLIENT_USE_SSL=y

    ビルドに成功すると sdk フォルダ直下に nuttx.spk ファイルが生成されます。

    tools/config.py feature/ethernet feature/vtun -m
    <追加のコンフィグを設定>
    make
    
  3. nuttx.spk を Spresense ボードへ書き込みます。

    この例では シリアルポートとして /dev/ttyUSB0 を、書き込み速度の baudrate に 500000 bps を設定しています。お使いの環境に合わせて変更してください。

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    

    ネットワーク機能として、 LTE(ALT1250): feature/lte, Wifi(GS2200m): feature/wifi, Ethernet(WIZnet): feature/ethernet がVTUNに対応しています。

30.3. 設定ファイルの転送

作成した vtund.conf ファイルを以下のコマンドでSpresenseに転送します。

./tools/flash.sh -w vtund.conf
xmodem
>>> Install files ...
nsh> xmodem /mnt/spif/vtund.conf
Install vtund.conf
|0%-----------------------------50%------------------------------100%|
######################################################################

nsh>

30.4. 動作確認

30.4.1. サーバ側

以下のコマンドでVTUNのデーモンをサーバモードで起動します。 (vtund_server.conf は作成した設定ファイルです。)

sudo vtund -s -f vtund_server.conf -n
vtund[21313]: VTUN server ver 3.X 07/30/2015 (standalone)

vtund: command not found となる場合はVTUNがサーバ上にインストールされていません。 以下のコマンドを使ってVTUNをインストールしてください。

sudo apt-get install vtund

30.4.2. クライアント(Spresesne)側

  1. シリアルターミナルを起動します。

    シリアルポートに /dev/ttyUSB0 を、baudrate に 115200 bps を指定して、 minicom ターミナルを使用する例を示します。

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. NuttShell でWIZnetのデーモンを起動します。

    nsh> wiznet &
     wiznet [8:50]
     ifcowiznet device configured
  3. vtun コマンドでVTUNのデーモンを起動します。

    nsh> vtun vpn 192.168.11.7 -f /mnt/spif/vtund.conf -n &
     vtun [11:100]
     Reloading configuration file
     VTun client ver 3.X 06/17/2020 started
     Connecting to 192.168.11.7
     Remote Server sends <TuK>
     .
     Session vpn[192.168.11.7] opened
     Created TUN device: tun0
     Couldn't run up commands: fork() not available
  4. ifconfig でネットワークインタフェースの状態を確認すると tun0 のアドレスが空で DOWN 状態であることがわかります。

    nsh> ifconfig
     tun0	Link encap:TUN at DOWN
     	inet addr:0.0.0.0 DRaddr:0.0.0.0 Mask:0.0.0.0
    
     eth0	Link encap:Ethernet HWaddr 02:1a:80:00:00:00 at UP
     	inet addr:192.168.11.8 DRaddr:192.168.11.1 Mask:255.255.255.0
  5. ifconfig コマンドでネットワークアドレスを指定し、インタフェースを有効にします。

    nsh> ifconfig tun0 192.168.254.2
    nsh> ifup tun0
     ifup tun0...OK
  6. 再度 ifconfig でネットワークインタフェースの状態を確認すると tun0 のアドレスが設定され UP 状態であることがわかります。この状態になると、 192.168.254.0 というネットワーク上で相互アクセス可能になります。

    nsh> ifconfig
     tun0	Link encap:TUN at UP
     	inet addr:192.168.254.2 DRaddr:192.168.254.1 Mask:255.255.255.0
    
     eth0	Link encap:Ethernet HWaddr 02:1a:80:00:00:00 at UP
     	inet addr:192.168.11.8 DRaddr:192.168.11.1 Mask:255.255.255.0
    この中の eth0 がローカルネットワークアドレス、 tun0 が仮想ネットワークアドレスになります。
  7. webserver コマンドでSpresense上でWebServerを立ち上げます。

    nsh> webserver
     Starting webserver
  8. サーバ側で http://192.168.254.2 (Spresense側のTUN0のIPアドレス) を curl コマンドで表示すると正しく表示され、サーバからSpresenseへアクセスできることがわかります。

    curl -x "" http://192.168.254.2
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title>Welcome to the uIP web server!</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body bgcolor="#fffeec" text="black">
    
    <div class="menu">
    <div class="menubox"><a href="/">Front page</a></div>
    <div class="menubox"><a href="files.shtml">File statistics</a></div>
    <div class="menubox"><a href="stats.shtml">Network statistics</a></div>
    <br>
    </div>
    
    <div class="contentblock">
    <p>
    These web pages are served by a small web server running on top of
    the <a href="http://www.sics.se/~adam/uip/">uIP embedded TCP/IP
    stack</a>.
    </p>
    <p>
    Click on the links above for web server statistics.
    </p>
    </div>
    </body>
    </html>
    

31. バックトレース機能について

ここではバックトレース機能を有効にして、スタックダンプのログからコールスタックを取得する方法や、dump_stack() 及び backtrace() 関数の使い方について解説します。

この機能を使うためには、SDK のコンフィグレーションに、feature/backtrace または feature/debug を追加してください。

31.1. コールツリーの取得方法

feature/backtrace または feature/debug コンフィグレーションを有効にした状態で、Hard Fault が発生したときに次のようなスタックダンプログが表示されます。

arm_hardfault: Hard Fault escalation:
arm_hardfault: PANIC!!! Hard Fault!:arm_hardfault:      IRQ: 3 regs: 0x2d044e14
arm_hardfault:  BASEPRI: 000000e0 PRIMASK: 00000000 IPSR: 00000003 CONTROL: 00000000
arm_hardfault:  CFSR: 00008200 HFSR: 40000000 DFSR: 00000000 BFAR: 05000000 AFSR: 00000000
arm_hardfault: Hard Fault Reason:
up_assert: Assertion failed at file:armv7-m/arm_hardfault.c line: 173 task: spresense_main
backtrace| 7: 0x0d029de6 0x0d01db40 0x0d01a964 0x0d01b43a 0x0d01deea 0x0d01e842 0x0d01a06a 0x0d0062d8
backtrace| 7: 0x0d0048ba
arm_registerdump: R0: 05000000 R1: 00000010 R2: 2d044ec4  R3: 00000000
arm_registerdump: R4: 2d03de70 R5: 2d03de70 R6: 00000000  FP: 05000000
arm_registerdump: R8: 00000001 SB: 05000000 SL: 00000000 R11: 0d033421
arm_registerdump: IP: 00000000 SP: 2d044ee8 LR: 0d0070b9  PC: 0d01db40
arm_registerdump: xPSR: 81000000 BASEPRI: 000000e0 CONTROL: 00000000
arm_registerdump: EXC_RETURN: ffffffe9
arm_dump_stack: IRQ Stack:
arm_dump_stack: sp:     2d036a88
arm_dump_stack:   base: 2d0362f8
arm_dump_stack:   size: 00000800
arm_stackdump: 2d036a80: 0d01db40 0d007339 40000000 00000003 2d044e14 00008200 00000001 05000000
arm_stackdump: 2d036aa0: 00000000 0d0061bb 00000080 0d001d73 40000000 00000000 05000000 00000000
arm_stackdump: 2d036ac0: 0d0004d1 00000000 2d037500 00000000 05000000 0d003aa7 00000000 0d001cb1
arm_dump_stack: User Stack:
arm_dump_stack: sp:     2d044ee8
arm_dump_stack:   base: 2d043110
arm_dump_stack:   size: 00001fd0
arm_stackdump: 2d044ee0: 00000000 0d01db0d 2d03de70 00000000 2d044f78 2d03de70 00000000 2d044f78
arm_stackdump: 2d044f00: ffffffff 00000002 00000000 0d0333b4 00000000 0d01a969 00000000 ffffffe9
arm_stackdump: 2d044f20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d044f40: 00000000 2d03de70 2d03e2dc 00000002 00000000 00000000 00000000 0d0333b4
arm_stackdump: 2d044f60: 00000000 0d01b43f 00000000 0d0003cb 2d03e2e8 00000000 2d03e2dc 2d03e2df
arm_stackdump: 2d044f80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d044fa0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d044fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d044fe0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d045000: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d045020: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
arm_stackdump: 2d045040: 00000000 00000000 00000000 00000000 2d03e2dc 2d03de70 2d042e68 2d03e2dc
arm_stackdump: 2d045060: 0d0333a6 00000001 00000000 00000000 00000000 0d01deef 2d03de70 00000001
arm_stackdump: 2d045080: 2d03de70 2d0430f8 00000000 00000000 00000000 0d01e847 2d0430f8 00000001
arm_stackdump: 2d0450a0: 00000001 0d01a06f 00000000 00000000 0d00cdad 00000064 00000000 0d00cdad
arm_stackdump: 2d0450c0: 00000000 0d0062db 2d0430f8 00000001 2d0430f8 0d0048bf 00000000 00000000
arm_showtasks:    PID    PRI     STACK      USED   FILLED    COMMAND
arm_showtasks:   ----   ----      2048       592    28.9%    irq
arm_dump_task:      0      0      1000       380    38.0%    Idle Task
arm_dump_task:      1    224      1992       588    29.5%    hpwork 0x2d037758
arm_dump_task:      2    100      1992       276    13.8%    lpwork 0x2d037768
arm_dump_task:      3    100      1992       276    13.8%    lpwork 0x2d037768
arm_dump_task:      4    100      1992       276    13.8%    lpwork 0x2d037768
arm_dump_task:      6    200       976       464    47.5%    cxd56_pm_task
arm_dump_task:      7    100      8144       716     8.7%    spresense_main
backtrace| 0: 0x0d00797e 0x0d00399e 0x0d003996 0x0d0002a0
backtrace| 1: 0x0d007efa 0x0d00400e 0x0d00400a 0x0d00402a 0x0d004a7a 0x0d0048a6
backtrace| 2: 0x0d007efa 0x0d00400e 0x0d00400a 0x0d00402a 0x0d004a7a 0x0d0048a6
backtrace| 3: 0x0d007efa 0x0d00400e 0x0d00400a 0x0d00402a 0x0d004a7a 0x0d0048a6
backtrace| 4: 0x0d007efa 0x0d00400e 0x0d00400a 0x0d00402a 0x0d004a7a 0x0d0048a6
backtrace| 6: 0x0d007efa 0x0d003d7c 0x0d003d78 0x0d003cea 0x0d001926 0x0d0062d8 0x0d0048ba
backtrace| 7: 0x0d029de6 0x0d03459b 0x0d003d75 0x0d01db40 0x0d01a964 0x0d01b43a 0x0d01deea 0x0d01e842
backtrace| 7: 0x0d01a06a 0x0d0062d8 0x0d0048ba

backtrace 行に PID 番号ごとのコールスタックアドレスが表示されます。

backtrace| PID番号: コールスタックアドレス

arm-none-eabi-addr2line コマンドを用いて、このログを取得したときのビルドイメージ nuttx の ELF ファイルと、backtrace 行に表示されたアドレスを入力することによって、コールツリーのソースコードの場所(ファイル名と行番号)を取得することができます。

$ arm-none-eabi-addr2line -e nuttx 0x0d029de6 0x0d01db40 0x0d01a964 0x0d01b43a 0x0d01deea 0x0d01e842 0x0d01a06a 0x0d0062d8 0x0d0048ba
/home/username/spresense/nuttx/arch/arm/src/common/arm_backtrace_thumb.c:485
/home/username/spresense/sdk/apps/nshlib/nsh_dbgcmds.c:255
/home/username/spresense/sdk/apps/nshlib/nsh_parse.c:741
/home/username/spresense/sdk/apps/nshlib/nsh_parse.c:2592
/home/username/spresense/sdk/apps/nshlib/nsh_session.c:217
/home/username/spresense/sdk/apps/nshlib/nsh_consolemain.c:106
/home/username/spresense/sdk/apps/system/nsh/nsh_main.c:153
/home/username/spresense/nuttx/libs/libc/sched/task_startup.c:70
/home/username/spresense/nuttx/sched/task/task_start.c:134

31.2. dump_stack() 関数の使い方

ソースコード中に、execinfo.h を include して、dump_stack() 関数を追加することで、

#include <execinfo.h>

  dump_stack();

ログ上に backtrace 行を表示することができます。

backtrace|21: 0x0d01f7ba 0x0d010440 0x0d0003e6 0x0d005794 0x0d0184c0 0x0d0184d0 0x0d003bba 0x0d006abe
backtrace|21: 0x0d01227c 0x0d005d90 0x0d004372

前述した方法で、arm-none-eabi-addr2line コマンドを用いて、dump_stack() を挿入した箇所への呼び出しパスを表示することができます。

$ arm-none-eabi-addr2line -e nuttx 0x0d01f7ba 0x0d010440 0x0d0003e6 0x0d005794 0x0d0184c0 0x0d0184d0 0x0d003bba 0x0d006abe 0x0d01227c 0x0d005d90 0x0d004372
/home/username/spresense/nuttx/arch/arm/src/common/arm_backtrace_thumb.c:485
/home/username/spresense/nuttx/libs/libc/sched/sched_dumpstack.c:69
/home/username/spresense/nuttx/arch/arm/src/chip/cxd56_serial.c:1007
/home/username/spresense/nuttx/drivers/serial/serial.c:1266
/home/username/spresense/nuttx/fs/vfs/fs_write.c:138
/home/username/spresense/nuttx/fs/vfs/fs_write.c:202
/home/username/spresense/nuttx/sched/semaphore/sem_post.c:224
/home/username/spresense/nuttx/include/nuttx/mutex.h:259
/home/username/spresense/sdk/apps/examples/hello/hello_main.c:40
/home/username/spresense/nuttx/libs/libc/sched/task_startup.c:70
/home/username/spresense/nuttx/sched/task/task_start.c:134

また、Linux Programmer’s Manual と同じ方法で、backtrace() 関数を使用することもできます。

backtrace_symbols() backtrace_symbols_fd() 関数も呼び出しは可能ですが、シンボル情報を表示することはできません。

32. GNSS RAMメモリ使用機能について

Spresense内蔵GNSS測位機能を使用しないユースケースにおいて、ユーザーアプリケーションから GNSS RAM (640KB) を汎用メモリとして利用することができます。

この機能を使うためには、ブートローダーのインストール より SDKv3.2.0以降のブートローダーへ更新してください。

32.1. 制約事項

GNSS RAM を使用するにあたって以下の制約事項があります。

  • 内蔵GNSS機能と同時に利用することはできません

    • 内蔵GNSSファームウェアが動作している場合、アプリケーションからGNSS RAMへアクセスするとHardfaultエラーが発生します。

    • 内蔵GNSS機能との排他なので、GNSS Add-onボードを利用している場合はこの制約の対象外です。

  • メモリアクセス速度が遅い

    • アプリケーションが通常利用している RAM に比べて 1/8 以下にパフォーマンスが落ちます。

    • メモリアクセスが低速でも構わない用途で使用してください。

  • ハードウェアDMAが直接アクセスするバッファとしては利用できません

    • Camera (CISIF) 用のバッファとしては使用できない

    • Audio 用のバッファとしては使用できない

32.2. GNSS RAMへコードやデータを配置する方法

ソースコードに GNSSRAM_CODEGNSSRAM_DATAGNSSRAM_BSS を付与することで、任意のコードやデータを GNSS RAM に配置することができます。 ユーザーは、これらのキーワードを含むプログラムをビルドして nuttx.spk を生成します。 実際のGNSS RAMへの配置は、起動時にブートローダーによって行われます。

サンプルコードを以下に示します。

#include <arch/chip/gnssram.h> (1)

static const int g_alloc_sizes[NTEST_ALLOCS] GNSSRAM_DATA = (2)
{
    1024,    12,    962,   5692, 10254,   111,   9932,    601,
    222,   2746,      3, 124321,    68,   776,   6750,    852,
    4732,    28,    901,    480,  5011,  1536,   2011,  81647,
    646,   1646,  69179,    194,  2590,     7,    969,     70
};

static void *g_allocs[NTEST_ALLOCS] GNSSRAM_BSS; (3)

static GNSSRAM_CODE void function(void) (4)
{

}
1 gnssram.h を include してください。
2 初期値付きデータ(data) に GNSSRAM_DATA を付与することで、GNSS RAM 上の data セクションに配置することができます。
3 初期値無しデータ(bss) に GNSSRAM_BSS を付与することで、GNSS RAM 上の bss セクションに配置することができます。
4 関数に GNSSRAM_CODE を付与することで、 GNSS RAM上のtext セクションに配置することができます。

関数や変数の単位ではなくライブラリ(アーカイブ)やオブジェクト単位でGNSS RAMへ配置したいときは、リンクスクリプトファイルを直接編集してください。

リンクスクリプトファイルの変更例を以下に示します。

    /* GNSS memory */

    .gnssram.text : {
        _sgnsstext = ABSOLUTE(.);

        /* Possible to locate text of any object file.
         * *libxxx.a:*.o(.text .text.*)
         * *libxxx.a:*.o(.rodata .rodata.*)
         */
        *libapps.a:*.o(.text .text.*) (1)
        *libapps.a:*.o(.rodata .rodata.*)

    } > gnssram

    .gnssram.data . : ALIGN(4) {
        /* Possible to locate data of any object file.
         * *libxxx.a:*.o(.data .data.*)
         */
        *libapps.a:*.o(.data .data.*) (2)

    } > gnssram

    .gnssram.bss . (NOLOAD) : {
        . = ALIGN(4);
        _gnssramsbss = ABSOLUTE(.);

        /* Possible to locate bss of any object file.
         * *libxxx.a:*.o(.bss .bss.*)
         * *libxxx.a:*.o(COMMON)
         */
        *libapps.a:*.o(.bss .bss.*) (3)
        *libapps.a:*.o(COMMON)

    } > gnssram
1 libapps.a 全オブジェクトのtextやrodataを GNSS RAM上の text セクションに配置することができます。
2 libapps.a 全オブジェクトのdata を GNSS RAM 上の data セクションに配置することができます。
3 libapps.a 全オブジェクトのbssを GNSS RAM 上の bss セクションに配置することができます。

ここの記述を変更することで、特定のオブジェクトファイルのみGNSS RAMに貼り付けることも可能です。 詳しくは、GNU ld のドキュメントを参照してください。

32.3. GNSS RAMをヒープ領域として利用する方法

up_gnssram_initialize() 関数を呼び出すことで、GNSS RAMメモリをヒープ領域として使用することができます。 前述した方法でコードやデータをGNSS RAMに配置している場合は、それらを除いた空きメモリをヒープ領域に割り当てます。

#include <arch/chip/gnssram.h> (1)

  up_gnssram_initialize(); (2)
1 gnssram.h を include してください。
2 GNSS RAMヒープ領域として使用するための初期化関数を呼び出します。

このヒープ領域を使用するための専用のメモリアロケート関数群が提供されています。 malloc/free の代わりに、以下の関数を呼び出すことで GNSS RAM上のヒープ領域を利用することができます。

#include <arch/chip/gnssram.h>

void *up_gnssram_malloc(size_t size);
void *up_gnssram_calloc(size_t n, size_t elem_size);
void *up_gnssram_realloc(void *ptr, size_t size);
void *up_gnssram_zalloc(size_t size);
void up_gnssram_free(void *mem);
void *up_gnssram_memalign(size_t alignment, size_t size);
struct mallinfo up_gnssram_mallinfo(void);

32.4. GNSS RAMを独自でメモリ管理する方法

extern int cxd56_gnssram_clock_enable(void); (1)

  cxd56_gnssram_clock_enable(); (2)
1 コンパイラのwarningを抑止するためにプロトタイプ宣言を追加します。
2 GNSS RAMを使用するために低階層のAPIを呼び出します。

この関数を実行することで、GNSS RAM (640KByte) のメモリへ自由にアクセスすることができます。 アプリケーションCPUからみたアドレス空間は、 0x09000000 ~ 0x090a0000 になります。