Developer World Spresense
日本語 中文
Table of Contents

1. Examples

The examples in the Spresense SDK are installed as a built-in command in the NuttShell environment. Refer to the README.txt file in the directory of each example for additional details about the required SDK configuration etc.

In SDK v2.0 or later, the original NuttX applications have also been added to Examples list.

(See here for tutorials in SDK old version 1.x.)

If you are building for the first time, please refer to the getting started guide. There are detailed instructions about how to configure and run the built program.

1.1. SDK examples

Category Example Description

Peripheral
Driver

adc_monitor

An example of how to read the A/D conversion data

GPS (GNSS)

gnss

An example of how to read GNSS sensor data

geofence

An example of how to set up and use a geofence

gnss_atcmd

An example of how to output NMEA sentences on the terminal using GNSS AT command.

gnss_factory

A utility application running the GNSS factory test

gnss_pvtlog

An example of how to use the GNSS PVTLOG

Audio

audio_player

An example of audio playback

audio_recorder

An example of audio recorder

audio_through

An example of how to set the audio-paths from microphone to speaker and I2S in/out

audio_pcm_capture

An example of how to capture PCM data

audio_recognizer

An example of audio recognizer framework

audio_beep

An example of audio beep

audio_dual_players

An example of audio dual recorder

audio_player_objif

An example of audio playback by using object interface layer

audio_recorder_objif

An example of audio recorder by using object interface layer

audio_pcm_capture_objif

An example of audio PCM capture by using object interface layer

audio_sound_effector

An example of audio sound effector with low delay

ASMP

asmp

An example of how to run worker program of multi cores on ASMP framework

prime

An example of prime calculation by using multi cores

fft

An example of FFT calculation by using multi cores

Tenserflow
Lite for Microcontroller

tf_example

An example of how to run Tenserflow LM examples(hello_world, micro_speech and person_detection)

Sensor

accel

An example of how to read the accelerometer sensor data

gyro

An example of how to read the gyro sensor data

light

An example of how to read the light sensor data

mag

An example of how to read the magnetic sensor data

press

An example of how to read the pressure sensor data

proximity

An example of how to read the proximity sensor data

colorsensor

An example of how to read the color sensor data

tilt

An example of how to detect tilt using the accelerometer sensor

decimator

An example of SCU decimator

step_counter

An example of step counter with activity recognition using the accelerometer sensor

Camera

camera

An example of camera

multi_webcamera

An example of multi web camera application

JPEG

jpeg_decode

An example of JPEG decoder

DNN

dnnrt_lenet

An example of number recognition using DNN Runtime

LTE

lte_http_get

An example of HTTP GET on LTE network

lte_tls

An example of TLS communication on LTE network

lte_websocket

An example of WebSocket communication on LTE network

lte_mqtt

An example of MQTT communication on LTE network

lte_lwm2m

An example of Lightweight M2M(LWM2M) communication on LTE network

lte_awsiot

An example of AWS IoT communication on LTE network

HostIF

hostif

An example of HostIF communication via I2C or SPI with an external host.

Others

setjmp

An example of setjmp()/longjmp() functions

1.2. NuttX examples

Category Example Description

Hello

hello

A "hello world" example application in C

helloxx

A "hello world" example application in C++

Peripheral
Driver

alarm

An example of how to set an RTC alarm

watchdog

An example of how to configure the watchdog

pwm

An example of how to output PWM (Pulse Width Modulation) signal

Graphics

nx

An example of NX graphics

nxhello

An example of drawing "Hello" text using NX graphics

nximage

An example of drawing bitmap using NX graphics

nxlines

An example of drawing line using NX graphics

nxtext

An example of drawing text using NX graphics

Network

ftpc

An example of FTP transfer (Client)

ftpd

An example of FTP transfer (Server)

tcpecho

An example of TCP echo transfer

tcpblaster

An example of TCP blaster transfer between server and client

Sensor

bmi160

An example of how to read the accelerometer/gyro sensor data

Battery

charger

An example of battery charger (It’s not supported on Spresense board)

Others

json

An example of JSON parser with cJSON library

inidumper

An example of ini parser with inih library

pdcurses

An example of PDCurses

embedlog

An example of embedded logging with embedlog library

2. Peripheral Driver Tutorials

2.1. RTC alarm example application

This section describes the usage of RTC alarm example application.

2.1.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Execute the configuration by specifying examples/alarm as an argument of config.py.
    If the build is successful, a nuttx.spk file will be created under the sdk directory.

    tools/config.py examples/alarm
    make
    
  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

2.1.2. Operation check

Open the serial terminal, and run alarm command.

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type alarm command on NuttShell prompt

    The usage of alarm command is shown below.

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

    <seconds> means the relative time (seconds). For example, alarm 5 will trigger the RTC alarm after 5 seconds.

    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. RTC alarm with power saving features

Here is an example of using the alarm command in combination with the power saving features.

Spresense provides power saving features such as Deep Sleep and Cold Sleep modes. It can enter these sleep states using the poweroff command. And, by the RTC alarm function, it can wake up from these sleep states.

For more information about Deep Sleep and Cold Sleep, refer to Sleep Mode.

2.1.3.1. Wake up from Deep Sleep mode

In the following example, an alarm is set after 10 seconds, and the system enters Deep Sleep mode by shutdown command.
After 10 seconds, the alarm is expires and the system wakes up from the Deep Sleep state.

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. Wake up from Cold Sleep mode

In the following example, an alarm is set after 10 seconds, and the system enters Cold Sleep mode by poweroff 1 command.
After 10 seconds, the alarm is expires and the system wakes up from the Cold Sleep state.

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. Other RTC commands

The date command allows you to set the RTC time and display the current RTC time.

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

e.g) set 2019/12/1 23:34:56 to RTC

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

The current time is displayed by the date command.

nsh> date
Dec 01 23:35:14 2019

The RTC time is kept during sleep modes such as Deep/Cold Sleep and rebooting by the reboot command. However, if the power supply is turned off or the reset button is pressed, the RTC time will be clear.

The following example shows that the RTC time is retained even after the system reboot with the reboot command.

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 example application

This section describes the usage of Watchdog example application.

2.2.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Execute the configuration by specifying examples/watchdog as an argument of config.py.
    If the build is successful, a nuttx.spk file will be created under the sdk directory.

    tools/config.py examples/watchdog
    make
    
  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

2.2.2. Operation check

Open the serial terminal, and run wdog command.

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type wdog command on NuttShell prompt

    The usage of wdog command is shown below.

    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

    Clear the watchdog timer by calling ioctl(fd, WDIOC_KEEPALIVE, 0) with the specified <pingdelay> period [msec].

    -p

    Keeps the watchdog clear during the specified <pingtime> period [msec].

    -t

    Set the period of watchdog timer by calling ioctl(fd, WDIOC_SETTIMEOUT, (unsigned long)wdog.timeout) with the specified <timeout> [msec].

This example application can confirm that the system reboots when the watchdog timer is expired.

The following is an example of running the wdog command.

If you type wdog command without argument, the period of watchdog timer is set to the default 2 seconds. During 5 seconds, the watchdog timer continues to be cleared at a cycle of 500 msec. After that, the watchdog timer will be expired and the system will reboot without clearing the watchdog timer.

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 example application

This section describes the usage of ADC example application.

2.3.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Execute the configuration by specifying examples/adc_monitor as an argument of config.py.
    If the build is successful, a nuttx.spk file will be created under the sdk directory.

    tools/config.py examples/adc_monitor
    make
    
  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

2.3.2. Operation check

Open the serial terminal, and run adc_monitor command.

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type adc_monitor command on NuttShell prompt

    The usage of adc_monitor command is shown below.

    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

    There are a total of 6 ADC dedicated pins. Specify -p /dev/lpadc[0-3] or /dev/hpadc[0-1] with the -p option. The relationship between pin numbers and device files on the Spresense board is shown below.

    Pin number

    A0

    A1

    A2

    A3

    A4

    A5

    /dev file

    /dev/lpadc0

    /dev/lpadc1

    /dev/lpadc2

    /dev/lpadc3

    /dev/hpadc0

    /dev/hpadc1

    -n

    Specify the number of measurements.

For example, you can read AD converted data from HPADC0(A4) 10 times. The data from HPADC0 is stored to the buffer and displays the average, minimum, and maximum values. ADC data is 16-bit signed data and the range is -32767 to 32767.

nsh> adc -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 sampling frequency

ADC sampling frequency depends on the clock selected by SCU clock mode.

tutorial scu clock
2.3.3.1. HPADC (High Performance ADC)

HPADC is an ADC capable of high-speed sampling.

The clock system diagram of HPADC is shown below.

diag 9ccfe34bb7113fe90709ffcf1dcc8c2e

The HPADC clock is determined by fixedly dividing the clock source. The sampling frequency is determined by dividing the ADC clock by a power of two.

The n value can be changed by the following SDK configuration.

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

The possible range of n value depends on the SCU clock mode.

  1. In case of SCU clock mode = RTC

    n 9 10 11

    Fs(Hz)

    64

    32

    16

    Available

  2. In case of 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 by default configuration, then Fs is 16KHz.
    (*2): If CONFIG_CXD56_HPADC0_HIGHSPEED=y, it supports Fs up to 512KHz.
    (*3): If CONFIG_CXD56_HPADC0_HIGHSPEED=y, Fs depends on the performance of the SCU sequencer, and is about 540-550KHz.
    (*4): If n is 0, the smoothing CIC filter is disabled and ADC outputs the raw value with 10bit resolution.

    If CONFIG_CXD56_HPADC0_HIGHSPEED is enabled,
    HPADC1, LPADC, and I2C/SPI SCU sequencers will not be available.

    The recommended configuration at the high-speed sampling rate (512KHz) is shown as below.

    Configuration Value Description

    CONFIG_CXD56_ADC

    y

    Enable ADC.

    CONFIG_CXD56_HPADC0

    y

    Enable HPADC0.

    CONFIG_CXD56_HPADC1

    n

    Disable HPADC1.

    CONFIG_CXD56_LPADC

    n

    Disable LPADC.

    CONFIG_CXD56_HPADC0_FREQ

    2

    Set the Fs to 512KHz.

    CONFIG_CXD56_HPADC0_HIGHSPEED

    y

    Enable the high-speed option.

    CONFIG_CXD56_HPADC0_INPUT_GAIN_M6DB

    y

    Set the input gain to -6dB.

    CONFIG_CXD56_I2C0_SCUSEQ

    n

    Disable I2C0 SCU sequencer.

    CONFIG_CXD56_I2C1_SCUSEQ

    n

    Disable I2C1 SCU sequencer.

    CONFIG_CXD56_SPI3_SCUSEQ

    n

    Disable SPI3 SCU sequencer.

    CONFIG_CXD56_SCU_XOSC

    y

    Set the SCU clock source to XOSC.

2.3.3.2. LPADC (Low Power ADC)

LPADC is an ADC that operates at a lower sampling rate but lower power consumption than HPADC. The clock system diagram of LPADC is shown below.

diag 5f3002e51d27ea8e330c733bd666b326

LPADC operates based on RTC clock. The sampling frequency is determined by dividing the clock by a power of two.

The n value can be changed by the following SDK configuration.

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

The possible range of n value depends on the SCU clock mode. LPADC has 4 channels in total. Depending on whether LPADC is used only 1 channel, 2 channels or 4 channels, the upper limit of sampling frequency is changed. The possible values of n in each case are shown below.

tutorial lpadc ch
  1. In case of SCU clock mode = RTC

    1. When any one of LPADC channels 0 to 3 is selected

      n 11 12 13 14 15

      Fs(Hz)

      16

      8

      4

      2

      1

      Available

    2. When two channels of LPADC channel 0 and 1 are selected

      n 12 13 14 15

      Fs(Hz)

      4

      2

      1

      0.5

      Available

    3. When four channels of LPADC channel 0,1,2 and 3 are selected

      n 11 12 13 14 15

      Fs(Hz)

      4

      2

      1

      0.5

      0.25

      Available

  2. In case of SCU clock mode = RCOSC

    1. When any one of LPADC channels 0 to 3 is selected

      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. When two channels of LPADC channel 0 and 1 are selected

      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. When four channels of LPADC channel 0,1,2 and 3 are selected

      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 all channels, SCU clock mode = RCOSC, n = 7 by default configuration

  3. In case of SCU clock mode = XOSC

    1. When any one of LPADC channels 0 to 3 is selected

      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. When two channels of LPADC channel 0 and 1 are selected

      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. When four channels of LPADC channel 0,1,2 and 3 are selected

      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 example application

This section describes the usage of PWM example application.

2.4.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Execute the configuration by specifying examples/pwm as an argument of config.py.
    If the build is successful, a nuttx.spk file will be created under the sdk directory.

    tools/config.py examples/pwm
    make
    
  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

2.4.2. Operation check

Open the serial terminal, and run pwm command.

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type pwm command on NuttShell prompt

    The usage of pwm command is shown below.

    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

    e.g) Outputs PWM signal with a frequency of 2000 Hz and a duty ratio of 30% to PWM1 during 10 seconds.

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

    There are a total of 4 PWM pins. Specify -p /dev/pwm[0-3] with the -p option.

    -f

    Set the PWM <frequency> [Hz].

    -d

    Set the <duty> ratio (the fraction of the high period of the period) [%] from 1 to 99.

    -t

    Output the PWM signal for the specified <duration> time [s].

2.4.3. PWM frequency and duty ratio

PWM frequency depends on the clock selected by SCU clock mode.

tutorial scu clock

The SCU clock is shown below.

  • Same with SCU32K → RTC 32.768kHz

  • RCOSC → approximately 8.2MHz

  • XOSC → 13MHz obtained by dividing TCXO 26MHz by CONFIG_CXD56_SCU_XOSC_DIV(=2)

The period of the PWM signal waveform is determined by the PWM_CYCLE count of the SCU clock as shown in the figure below. The Low output period is determined by the PWM_THRESH count. The upper limit of the count is 0xffff.

tutorial pwm

The PWM frequency range is:

1 <= PWM frequency <= SCU clock / 2

For example, if RCOSC is selected for the SCU clock, the frequency will be from 1 Hz to approximately 4 MHz.

Regarding the duty ratio, the low and high periods are calculated from the approximate value with the specified -d option. Therefore the output waveform does not have an accurate duty ratio and may include rounding errors.

3. GPS Tutorials

3.1. Sample Application of GPS(GNSS)

This chapter shows the operation procedure of GPS(GNSS) sample application.

3.1.1. Build & Flash

  1. Move to the folder where you cloned the Spresense SDK, and enter the sdk folder name:

    cd spresense/sdk
    
  2. Set up the SDK configuration To enable the gnss example application, select examples/gnss.

    tools/config.py examples/gnss
    
  3. Build the example image:

    make
    

A nuttx.spk file will be created in the sdk folder after make has successfully finished.

  1. Just the same as Hello World example, flash the nuttx.spk to Spresense with tools/flash.sh.

    tools/flash.sh -c /dev/ttyUSB0 nuttx.spk
    
  2. When flashing the board is completed the board is restarted automatically.

3.1.2. GPS operation confirmation

Loading nuttx.spk to Spresense, you can run the GNSS program.

Open the serial terminal.
minicom -D /dev/ttyUSB0 -b 115200 -s

Execute gnss command, the gnss is a built-in application. The following text will be displayed:

tutorial gnss log1
Figure 1. GNSS startup log

If positioning is not available, you see this message:

No Positioning Data

And the time is displayed that is from 0 o’clock count up when GNSS start.
If the Spresense can receive GPS signals from the satellites (clear view to the sky etc), the time in UTC will be displayed in approximately 1 minute, and the GPS position in approximately 3 minutes.

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

Similar text as shown above is displayed, and latitude and longitude can be read.

4. Audio Tutorials

4.1. Sample Application of Audio Player

This chapter shows the operation procedure of the sample application of Audio Player.

4.1.1. Build & Flash

Here shows the build process by using command line.

  1. Move to the sdk directory:

    Run build-env.sh script provides tab keyword complementation of config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. Configure and build SDK.

    Set examples/audio_player as argument of config.py and execute configuration. When build succeeded, nuttx.spk binary file will be generated under sdk directory.

    tools/config.py examples/audio_player
    make
    
  3. Load nuttx.spk to Spresense board.

    In this case, serial port is /dev/ttyUSB0 and baudrate is 500000 bps, both are set.
    This parameter should be set to fit to your environment.

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
  4. For Audio Player, it is necessary to load the DSP binary for decode. You can choose to place the DSP binary on either a SD card or SPI-Flash. Here is how to load from the SD card.

    Specify the path of DSP binary in the application code ( audio_player_main.cxx ). In audio_player_main.cxx , it is specified by DSPBIN_FILE_PATH .

    #define DSPBIN_FILE_PATH "/mnt/sd0/BIN"

    This code shows that the SD card is selected.

    If you would like to use SPI-flash, please specify /mnt/spif/BIN .

    When you read SD card on PC, /mnt/sd0/BIN will appear as BIN/ under the root directory.
    Create this directory and place the DSP for the required codec here.

    When you want to decode MP3 files,
    select MP3DEC under spresense/sdk/modules/audio/dsp/

  5. Write the music file which you would like to play to the SD card. audio_player_main.cxx is specified in PLAYBACK_FILE_PATH .

    #define PLAYBACK_FILE_PATH "/mnt/sd0/AUDIO"

    Therefore, please insert SD card to PC and create AUDIO directory under the root of SD card. Next, put the audio files in AUDIO directory. It can also be placed in subdirectories.

  6. The current Audio Player sample is playing a simple PlayList. So, specify the location and file name of the playlist file and play music files. In audio_player_main.cxx , specify the path with PLAYLIST_FILE_PATH and the file name is specified in PLAYLIST_FILE_NAME .

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

    Please create PLAYLIST/ under root directory of SD card, and put TRACK_DB.CSV into there.

    For the contents of TRACK_DB.CSV , see README.txt under spresense/sdk/modules/audio/playlist/ .

Then you can play your playlist.

4.1.2. Operation check of Audio Player

When this nuttx.spk is loaded to the Spresense board, the Audio Player program can be executed.

Open the serial terminal as you did in the Hello sample.

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

When you run the audio_player app that was builtin,

tutorial player log
Figure 2. The log of music playback.

The log is displayed and the audio is played back.

If an error occurs, refer to Error Information of Audio SubSystem.

4.1.3. Appendix : Customize audio signal process

So far, you could run the audio_player application with this tutorial.
From here on, it explains about optional function which add custom signal process.

In AudioPlayer sample, you can perform your own signal processing on the playing audio.
If you would like to do this, you need to enable [Use postprocess] in the config menu.

  1. Enable Postprocess.

    Open the config menu.

    tools/cofig.py -m
    

    Check [Use Postprocess] .

    [Examples]
      [Audio player example]
        [Use Postprocess]        <= Y
    For more information on Postprocess , please refer to SDK Developer Guide Set preprocess.
  2. Do builds

    make
    

    When build completed successfully, POSTPROC binary file will be generated under spresense/examples/audio_player/worker/ .
    Please put this file on /mnt/sd0/BIN (If you read SD card on PC, it’s BIN/ ).

    In this sample application, POSTPROC includes a simple RCfilter by default.
    If you want to customize your own signal processing etc, please refer to here.

    Please playback audio files with Postprocess Enable/Disable, and check difference. Refer How to playback.

    == About customizing DSP binary (POSTPROC)

This chapter shows how to customize the DSP binary (POSTPROC).

4.1.3.1. Step 1. Edit the code of POSTPROC

Describes the code structure of POSTPROC and the editing location.
The code is divided into two parts: the part to edit the user and the part provided as a framework.

user-edited code

It is a code that users should edit mainly.
It can make unique signal processing by editing these codes.

The DSP code is in the worker directory, which has the userproc directory.
The user writes signal processing only in the userproc directory, and other things basically do not need to be changed.

Since main.cpp provides startup processing and data communication control with Main CPU, do not change it.

diag 8aa71a9d5e8bc8994523bdbc45dd927d
Figure 3. The structure of source code
main.cpp

Startup processing and DSP communication processing are written. There is no need to edit.

userproc_command.h

It is a header file that defines the communication command with DSP.
Describe your necessary parameters in this file.

userproc.h

The header file of user code.

userproc.cpp

The source file of user code.
Write or call signal processing to this file.

APIs are provided for user code

userproc.cpp provides a framework for Init , Exec , Flush , Set commands.
The user code can support the processing in DSP by writing the unique contents.

Describes the process that the user should write.
(* By default, an RC filter is included as a sample.)

This framework assumed that the state transition inside DSP like in the figure below.

diag d2c5818d86190851c02fe61bf2c42886
Figure 4. the state transition in DSP

Program the process by each command as following flow.

  1. DSP starts when AUDCMD_INIT_OUTPUTMIXER is called.

  2. Set necessary parameters (number of channels, bit length, etc.) with the Init command.

  3. When recording starts, the captured audio data is periodically sent to the DSP with the Exec command, so it can do a unique filter processing.

  4. If you want to change DSP internal parameters at any time, you can use Set command .The execution timing of this command is in the order of command reception including Exec .

  5. When recording stop, the Flush command is sent after the last audio data on Exec , so if termination processing is necessary, the processing is performed here.

Definition of command

The data types used by each function are described in userproc_command.h , and the contents can be freely written.

The format of each command is as shown below.
The minimum required parameters are placed in the top white area. Please do not change these.

The part of User param (purple part)in the figure below in userproc_command.h , you should define your parameters.

diag 3d57cbbb80b2fb16206e83f6942e25e8
Figure 5. the format of command

Each command is discribed in the following.

struct InitParam : public CustomprocCommand::CmdBase
  • Parameter for Init processing.
    All parameter defined by reserved , so you will change them to necessary parameters such as the number of channels and bit length.

struct ExecParam : public CustomprocCommand::CmdBase
  • Parameter for Exec processing.
    The address and size of audio data is defined in CustomprocCommand::ExecParamBase from which it is inherited as in ExecParam in the figure above.
    For details, see sdk/modules/include/audio/dsp_framework/customproc_command_base.h .

struct FlushParam : public CustomprocCommand::CmdBase
  • Parameter for Flush processing.
    The address and size of audio data is defined in CustomprocCommand::FlushParamBase from which it is inherited as in ExecParam in the figure above.
    For details, see sdk/modules/include/audio/dsp_framework/customproc_command_base.h .

struct SetParam : public CustomprocCommand::CmdBase
  • Parameter for Set processing.
    Define various dynamically changed parameters. By default, RC filter On/Off and coefficients are defined as sample.

Each functions

The following functions are written in userproc.cpp . The contents can be written freely.
Processing is performed according to each command definition.

void UserProc::init(InitParam *)
  • Write your initialize processing according to InitParam.
    It is executed by [AUDCMD_INIT_MPP] command from application code.
    (Nothing is done by default)

void UserProc::exec(ExecParam *)
  • Write your signal processing according to ExecParam. When you start recording, it will be called periodically from the SDK.
    1 frame is 640 samples when recording setup is LPCM, 1152 for MP3 (but 1728 at 16 kHz) samples.
    Get data from the input data address, do your signal processing, and write to the output data address. (RC filtering is written by default)

void UserProc::flush(FlushParam *)
  • Write the flush (termination) process according to FlushParam.
    when recording stops, it will be called only once from the SDK.
    For example, If it is delay filter like IIR or FIR filter, flush may be needed as filer clear.
    If there is data to be output, write to the output data address.
    (Nothing is done by default)

void UserProc::set(SetParam *)
  • Write the set (change parameter) process according to SetParam.
    It is executed by AUDCMD_SETMPPPARAM command from application code.
    (By default, the RC filter coefficient is set.)

4.1.3.2. Step 2. Build POSTPROC binary

If you enable Postprocess in configuration, POSTPROC binary will be created automatically when this application is built.
The path created is POSTPROC under spresense/examples/audio_player/worker .
Put this in the /mnt/sd0/BIN ( \BIN viewed from the PC) folder on the SD card.

4.2. Sample application of Audio Recorder

This chapter shows the operation procedure of the sample application of Audio Recorder.

4.2.1. Build & Flash

Here shows the build process by using command line.

  1. Move to the sdk directory:

    Run build-env.sh script provides tab keyword complementation of config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. Configure and build the kernel.

    In this case, release-defconfig is selected for kernel configuration.
    If you already built the kernel, you can skip this process.

    tools/config.py --kernel release
    make buildkernel
    
  3. Configure and build SDK.

    Set examples/audio_recorder as argument of config.py and execute configuration. When build succeeded, nuttx.spk binary file will be generated under sdk directory.

    tools/config.py examples/audio_recorder
    make
    
  4. Load nuttx.spk to Spresense board.

    In this case, serial port is /dev/ttyUSB0 and baudrate is 500000 bps, both are set.
    This parameter should be set to fit to your environment.

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
  5. For Audio Recorder, it is necessary to load the DSP binary for encoding. You can choose to place the DSP binary on either an SD card or SPI-Flash. Here is how to load from the SD card.

    Specify the path of DSP binary in the application code. In audio_recorder_main.cxx , it is specified by DSPBIN_PATH .

    #define DSPBIN_PATH "/mnt/sd0/BIN"

    This code shows that the SD card is selected.

    If you would like to use SPI-flash, please specify /mnt/spif/BIN .

    When you read SD card on PC, /mnt/sd0/BIN will appear as BIN/ under the root directory.
    Create this directory and place the DSP for the required codec here.

    When you would like to encode MP3 files,
    select MP3ENC file which is placed under spresense/sdk/modules/audio/dsp/

    The combinations of Codec type and DSP binary for other encoding are shown in the table below.

    Codec DSP Binary

    MP3

    MP3ENC

    LPCM

    SRC

4.2.2. Operation check of Audio Recorder

When this nuttx.spk is loaded to the Spresense board, the Audio recorder program can be executable.
Open the serial terminal as you did in the Hello sample.

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

When you run the audio_recorder app that was builtin,

tutorial recorder log
Figure 6. The log of sound recording.

The log is displayed and the audio is recording.

The recorded audio can be played back on a PC. At that time, there is an audio file in REC/ under the SD card root directory. In audio_recorder_main.cxx, the record file path is RECFILE_ROOTPATH , please change the application code as needed.

#define RECFILE_ROOTPATH "/mnt/sd0/REC"

+ This code shows that the audio data will be recorded in REC/ directory on the SD card.

+

If an error occurs, refer to Error Information of Audio SubSystem.

4.2.3. Appendix : Customize audio signal process

So far, you could run the audio_recorder application with this tutorial.
From here on, it explains about optional function which add custom signal process.

In AudioRecorder sample, you can perform your own signal processing on the recorded audio.
If you would like to do this, you need to enable [Use preprocess] in the config menu.

  1. Enable Preprocess.

    Open the config menu.

    tools/cofig.py -m
    

    Check [Use preprocess] .

    [Examples]
      [Audio recorder example]
        [Use preprocess]        <= Y
    For more information on Preprocess , please refer to SDK Developer Guide Set preprocess.
  2. Do builds

    make
    

    When build completed successfully, PREPROC binary file will be generated under spresense/examples/audio_recorder/worker/src .
    Please put this file on /mnt/sd0/BIN (If you read SD card on PC, it’s BIN/ ).

    In this sample application, PREPROC includes a simple RCfilter by default.
    If you want to customize your own signal processing etc, please refer to here.

    Please playback audio files which are recorded by Preprocess Enable/Disable, and check difference. Refer How to record and playback.

    == About customizing DSP binary (PREPROC)

this chapter shows how to customize the DSP binary (PREPROC).

4.2.3.1. Step 1. Edit the code of PREPROC

Describes the code structure of PREPROC and the editing location.
The code is divided into two parts: the part to edit the user and the part provided as a framework.

user-edited code

It is a code that users should edit mainly.
It can make unique signal processing by editing these codes.+

The DSP code is in the worker directory, which has the userproc directory.
The user writes signal processing only in the userproc directory, and other things basically do not need to be changed.

Since main.cpp provides startup processing and data communication control with Main CPU, do not change it.

diag c71976e8d3f7c4f35c0f3ae7027a0546
Figure 7. The structure of source code
main.cpp

Startup processing and DSP communication processing are written. There is no need to edit.

userproc_command.h

It is a header file that defines the communication command with DSP.
Describe your necessary parameters in this file.

userproc.h

The header file of user code.

userproc.cpp

The source file of user code.
Write or call signal processing to this file.

APIs are provided for user code

userproc.cpp provides a framework for Init , Exec , Flush , Set commands.
The user code can support the processing in DSP by writing the unique contents.

Describes the process that the user should write.
(* By default, an RC filter is included as a sample.)

This framework assumed that the state transition inside DSP like in the figure below.

diag ef5558636e0954129c76b34365bf28e3
Figure 8. the state transition in DSP

Program the process by each command as following flow.

  1. DSP starts when AUDCMD_INIT_MICFRONTEND is called.

  2. Set necessary parameters (number of channels, bit length, etc.) with the Init command.

  3. When recording starts, the captured audio data is periodically sent to the DSP with the Exec command, so it can do a unique filter processing.

  4. If you want to change DSP internal parameters at any time, you can use Set command .The execution timing of this command is in the order of command reception including Exec .

  5. When recording stop, the Flush command is sent after the last audio data on Exec , so if termination processing is necessary, the processing is performed here.

Definition of command

The data types used by each function are described in userproc_command.h , and the contents can be freely written.

The format of each command is as shown below.
The minimum required parameters are placed in the top white area. Please do not change these.

The part of User param (purple part)in the figure below in userproc_command.h , you should define your parameters.

diag 3d57cbbb80b2fb16206e83f6942e25e8
Figure 9. the format of command

Each command is discribed in the following.

struct InitParam : public CustomprocCommand::CmdBase
  • Parameter for Init processing.
    All parameter defined by reserved , so you will change them to necessary parameters such as the number of channels and bit length.

struct ExecParam : public CustomprocCommand::CmdBase
  • Parameter for Exec processing.
    The address and size of audio data is defined in CustomprocCommand::ExecParamBase from which it is inherited as in ExecParam in the figure above.
    For details, see sdk/modules/include/audio/dsp_framework/customproc_command_base.h .

struct FlushParam : public CustomprocCommand::CmdBase
  • Parameter for Flush processing.
    The address and size of audio data is defined in CustomprocCommand::FlushParamBase from which it is inherited as in ExecParam in the figure above.
    For details, see sdk/modules/include/audio/dsp_framework/customproc_command_base.h .

struct SetParam : public CustomprocCommand::CmdBase
  • Parameter for Set processing.
    Define various dynamically changed parameters. By default, RC filter On/Off and coefficients are defined as sample.

Each functions

The following functions are written in userproc.cpp . The contents can be written freely.
Processing is performed according to each command definition.

void UserProc::init(InitParam *)
void UserProc::exec(ExecParam *)
  • Write your signal processing according to ExecParam. When you start recording, it will be called periodically from the SDK.
    1 frame is 768 samples when recording setup is LPCM, 1152 for MP3 (but 1728 at 16 kHz) samples.
    Get data from the input data address, do your signal processing, and write to the output data address. (RC filtering is written by default)

void UserProc::flush(FlushParam *)
  • Write the flush (termination) process according to FlushParam.
    when recording stops, it will be called only once from the SDK.
    For example, If it is delay filter like IIR or FIR filter, flush may be needed as filer clear.
    If there is data to be output, write to the output data address.
    (Nothing is done by default)

void UserProc::set(SetParam *)
  • Write the set (change parameter) process according to SetParam.
    It is executed by AUDCMD_SET_PREPROCESS_DSP command from application code.
    (By default, the RC filter coefficient is set.)

4.2.3.2. Step 2. Build PREPROC binary

If you enable Preprocess in configuration, PREPROC binary will be created automatically when this application is built.
The path created is PREPROC under spresense/examples/audio_recorder/worker/src .
Put this in the /mnt/sd0/BIN ( \BIN viewed from the PC) folder on the SD card.

4.3. Sample application of Audio Recognizer

This chapter shows the operation procedure of the sample application of Audio Recognizer.

4.3.1. Build & Flash

Here shows the build process by using command line.

  1. Move to the sdk directory:

    Run build-env.sh script provides tab keyword complementation of config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. Configure and build the kernel.

    In this case, release-defconfig is selected for kernel configuration.
    If you already built the kernel, you can skip this process.

    tools/config.py --kernel release
    make buildkernel
    
  3. Configure and build SDK.

    Set examples/audio_recognizer as argument of config.py and execute configuration. When build succeeded, nuttx.spk binary file will be generated under sdk directory.

    tools/config.py examples/audio_recognizer
    make
    
  4. Load nuttx.spk to Spresense board.

    In this case, serial port is /dev/ttyUSB0`and baudrate is `500000 bps, both are set.
    This parameter should be set to fit to your environment.

    tools/flash.sh -c /dev/ttyUSB0 -b 500000 nuttx.spk
    
  5. For Audio Recognizer, it is necessary to load the DSP binary for recognizing. You can choose to place the DSP binary on either an SD card or SPI-Flash. Here is how to load from the SD card, put it to /mnt/sd0/BIN .

    When you read SD card on PC, /mnt/sd0/BIN will appear as BIN/ under the root directory.
    Create this directory and place the DSP for the recognizer binary here.

    This application uses recognizer DSP binary which is costumed by user.
    The binary RCGPROC will be generated on spresense/examples/audio_recognizer/worker_recognizer/ .
    If you would like to custom recognizing process, please refer How to custom RecognizerPROC.

    == Operation check of Audio Recognizer

When this nuttx.spk is loaded to the Spresense board, the Audio Recognizer program can be executable.

Open the serial terminal as you did in the Hello sample.

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

When you run the audio_recognizer app that was builtin,

tutorial recognizer log
Figure 10. The log of sound recognize.

The log is displayed and the audio is recognize.

The recognition result is received by callback function in application code audio_recognizer_main.cpp .
The parameter structure depends on RecognizerDSP. Please refer RecognizerPROC.
Result data will be received with MemoryHandle, therefore, you can get data by accessing to the address.

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

+ If an error occurs, refer to Error Information of Audio SubSystem.

4.3.2. Appendix : Custom audio signal process

So far, you could run the audio_recognizer application with this tutorial.
From here on, it explains about optional function which add custom signal process.

A captured audio data is 48kHz or 192kHz, and bit width is 16bit or 32bit. In AudioRecognizer sample, you can perform your own signal processing to fit to input of recognition library.
If you want to do this you need to enable [Use preprocess] in the config menu.

  1. Enable Preprocess.

    Open the config menu.

    tools/cofig.py -m
    

    Check [Use preprocess] .

    [Examples]
      [Audio recognizer example]
        [Use preprocess]        <= Y
    For more information on Preprocess, please refer to SDK Developer Guide Set preprocess.
  2. Do builds

    make
    

    When build completed successfully, PREPROC binary file will be generated under spresense/examples/audio_recognizer/worker/src .
    Please put this file on /mnt/sd0/BIN (If you read SD card on PC, it’s BIN/ ).

    In this sample application, PREPROC includes a simple RCfilter by default.
    If you want to customize your own signal processing etc, please refer to here.

    == About customizing of DSP binary

The audio_recognizer example uses two DSP binaries. For preprocess ( PREPROC ), and recognizer ( RCGPROC ).
this chapter shows how to customize the DSP binary.

4.3.2.1. Step 1. Edit the code of PREPROC, RCGPROC

Describes the code structure of PREPROC , RCGPROC and the editing location.
The code is divided into two parts: the part to edit the user and the part provided as a framework.

User-edited code

It is a code that users should edit mainly.
It can make unique signal processing by editing these codes.+

The DSP code is in the worker_preprocess and worker_recognizer directory, which has the userproc directory.
The user writes signal processing only in the userproc directory, and other things basically do not need to be changed.

Since main.cpp provides startup processing and data communication control with Main CPU, do not change it.

diag 850a31a07cc5b356f3b4ccee57c354cb
Figure 11. The structure of source code
main.cpp

Startup processing and DSP communication processing are written. There is no need to edit.

userproc_command.h

It is a header file that defines the communication command with DSP.
Describe your necessary parameters in this file.

userproc.h

The header file of user code.

userproc.cpp

The source file of user code.
Write or call signal processing to this file.

rcgproc_command.h

The header file which defines communication command with Recognition DSP. Describe your necessary parameters in this file.

rcgproc.h

The header file of Recognition DSP user code.

rcgproc.cpp

The source file of Recognition DSP user code.
Write or call signal processing to this file.

APIs are provided for user code

userproc.cpp, rcgproc.cpp provides a framework for Init , Exec , Flush , Set commands.
The user code can support the processing in DSP by writing the unique contents.

Describes the process that the user should write.
(* By default, an RC filter is included as a PREPROC sample.)

This framework assumed that the state transition inside DSP like in the figure below.

diag 842a41fa8a2406d220f0266e0a021ff6
Figure 12. The state transition in DSP

Program the process by each command as following flow.

  1. DSP starts when AUDCMD_INIT_RECOGNIZER is called.

  2. Set necessary parameters (number of channels, bit length, etc.) with the Init command.

  3. When recognizing starts, the captured audio data is periodically sent to the DSP with the Exec command, so it can do a unique recognition processing.

  4. If you want to change DSP internal parameters at any time, you can use Set command .The execution timing of this command is in the order of command reception including Exec .

  5. When recognizing stop, the Flush command is sent after the last audio data on Exec , so if termination processing is necessary, the processing is performed here.

Definition of command

The data types used by each function are described in userproc_command.h (for PREPROC) and rcgproc_command.h (for RCGPROC), and the contents can be freely written.

The format of each command is as shown below, and it has no difference between PREPROC and RCGPROC .
The minimum required parameters are placed in the top white area. Please do not change these.
The part of User param (purple part)in the figure below in userproc_command.h , you should define your parameters.

notification is a notification flag of Exec command. When you set it to except zero, result is notified to application.
For examples, you can request a reply from recognizer only when recognized result is changed.

diag 18441ee8379c82811c49595c4070498f
Figure 13. the format of command

Each command is described in the following.

struct InitRcgParam : public CustomprocCommand::CmdBase
  • Parameter for Init processing.
    Number of channels and bit width is defined as default. Please change required parameter.

struct ExecRcgParam : public CustomprocCommand::CmdBase
  • Parameter for Exec processing.
    The address and size of audio data is defined in CustomprocCommand::ExecParamBase from which it is inherited as in ExecParam in the figure above.
    For details, see sdk/modules/include/audio/dsp_framework/customproc_command_base.h .

struct FlushParam : public CustomprocCommand::CmdBase
  • Parameter for Flush processing.
    The address and size of audio data is defined in CustomprocCommand::FlushParamBase from which it is inherited as in ExecParam in the figure above.
    For details, see sdk/modules/include/audio/dsp_framework/customproc_command_base.h .

struct SetParam : public CustomprocCommand::CmdBase
  • Parameter for Set processing.
    Define various dynamically changed parameters. As default, flag of enable/disable recognition is defined.

Each functions

The following functions are written in rcgproc.cpp . The contents can be written freely.
Processing is performed according to each command definition.

void RcgProc::init(InitRcgParam *)
void RcgProc::exec(ExecRcgParam *)
  • Write your signal processing according to ExecParam. When you start recognizer, it will be called periodically from the SDK.
    At this sample application, 1 frame is 320 samples. (You can change this value in application code).
    Get data from the input data address, do your recognizer processing, and write to the output data address. (As default, output max/min/average value of audio frame.)

void RcgProc::flush(FlushRcgParam *)
  • Write the flush (termination) process according to FlushParam.
    when recognizer stops, it will be called only once from the SDK.
    For example, If recognize process has a delay, do flush to flush a delayed data after last frame.
    If there is data to be output, write to the output data address.
    (Nothing is done by default)

void RcgProc::set(SetRcgParam *)
  • Write the set (change parameter) process according to SetParam.
    It is executed by AUDCMD_SET_RECOGNIZER_DSP command from application code.
    (By default, enable flag of recognition process is set.)

4.3.2.2. Step 2. Build PREPROC, RCGPROC binary

If you enable Preprocess in configuration, PREPROC binary will be created automatically when this application is built.
The PREPROC will be generated under worker_preprocess , and the RCGPROC will be generated under worker_recognizer . Put them in to the /mnt/sd0/BIN ( \BIN viewed from the PC) folder on the SD card.

5. TensorFlow Tutorials

5.1. TensorFlow Lite for Microcontrollers: code examples

TensorFlow Lite for Microcontrollers (TensorFlow LM) is a version of Google’s TensorFlow end-to-end open source platform for machine learning. TensorFlow LM is designed to run machine learning models on microcontrollers and other devices as it only uses a few kilobytes of memory. Spresense SDK version 2.1.0 and later support TensorFlow LM.

There are three TensorFlow sample code examples provided that run using Spresense SDK v2.1.0. This chapter describes how to execute the sample code on Spresense equipment.

The three TensorFlow LM code examples are called:

  • hello_world

  • micro_speech

  • person_detection

5.1.1. Spresense equipment required

The equipment required to run each example is shown in the following table:

Example name Equipment Picture

hello_world

1. Spresense Main Board

tflm helloworld equipment

micro_speech

1. Spresense Main Board
2. Spresense Extension Board
3. Analog Mic

tflm micro speech equipment

person_detection

1. Spresense Main Board
2. Spresense Camera Board

tflm person detection equipment

5.1.2. Overview of TensorFlow LM on Spresense SDK

If you enable TensorFlow LM in Kconfig in Spresense SDK, the source code will be downloaded during the build process, and built as a library archive. If specified in Kconfig, the TensorFlow LM source code will be downloaded to externals/tensorflow after the build is executed. The build uses the same build system provided by TensorFlow LM.

For SDK v2.1.0, the Git SHA-1 of the TensorFlow LM to download is "372e7eef27e03adabceb4c7ca41d366776573a731".

When you select a TensorFlow LM example in the Spresense Kconfig, the corresponding example in the TensorFlow LM will also be built together as a library. Each TensorFlow LM example is wrapped into a command called tf_example so that it can be run as a command in NuttShell, the NuttX command used by the Spresense SDK. Since the setup() and loop() functions are implemented for the TensorFlow LM example to be run on Arduino, these functions are called in the tf_example.

The relationship between Spresense SDK, TensorFlow LM, and the code examples are shown in the following diagram:

tflm structure

The blue boxes are provided by TensorFlow LM, and the green boxes are provided by Spresense SDK.

5.1.3. Directory location of sample code

The source code for each example is contained in the directories as follows:

  • tf_example NuttShell command:

    • examples/tf_example

  • Code for using Spresense’s camera and Audio functions

    • externals/tensorflow

  • hello_world of TensorFlow LM:

    • externals/tensorflow/tensorflow-{Git SHA-1}/tensorflow/lite/micro/examples/hello_world

  • micro_speech of TensorFlow LM:

    • externals/tensorflow/tensorflow-{Git SHA-1}/tensorflow/lite/micro/examples/micro_speech

  • person_detection of TensorFlow LM:

    • externals/tensorflow/tensorflow-{Git SHA-1}/tensorflow/lite/micro/examples/person_detection

The directory tensorflow-{Git SHA-1} will download and extract when the build sequence is complete.
And the {Git SHA-1} is "372e7eef27e03adabceb4c7ca41d366776573a731" in SDK v2.1.0 case.

5.1.4. How to build the sample code

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

There are two steps in the build process:

  1. Configuring the example

  2. Executing the "make" to build the example.

The TensorFlow LM examples cannot be built in the Windows OS environment. This is because the Spresense SDK build system on the Windows OS is based on MSYS2, and the TensorFlow build system does not work on MSYS2 in Windows.

Step 1: Configuring the example
  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. Configuration

    Execute the configuration by specifying for each example as an argument of config.py.

    tools/config.py {default config name}
    

    For each example, replace {default config name} with the string shown in the Default config name column in the following table:

    Example name Default config name

    hello_world

    examples/tf_example_helloworld

    micro_speech

    examples/tf_example_micro_speech

    person_detection

    examples/tf_example_persondetect

    For example, if you want to build the "micro_speech" example, the command should be:

    tools/config.py examples/tf_example_micro_speech
    
5.1.4.1. Step 2: Executing to make nuttx.spk

When configuration is complete, execute the "make" command to build nuttx.spk.

make

If the build is successful, a nuttx.spk file will be created under the sdk directory.

5.1.5. How to flash the nuttx.spk

Use the following command to flash the nuttx.spk. In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Modify the command as required by your environment.

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

5.1.6. How to run the examples

  1. Open the serial terminal.

    Here, we use a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type tf_example command on NuttShell prompt

    nsh> tf_example

    The results of the operation will be output for each example.

Example result: hello_world

The following line is displayed continuously.

x_value: xxxxxx, y_value: xxxxxxx
Example result: micro_speech

Speak "Yes" or "No" into the MEMS Mic. Any result recognized is output on the terminal as follows.

Heard unknown (xxx) @ xxxxxx
Heard yes (xxx) @ xxxxxx
Heard no (xxx) @ xxxxxx
5.1.6.1. Example result: person_detection

By pointing the camera at a person’s face or at something other than a person’s face, the results of the recognition will be displayed, as shown below.

person score:xxx no person score:xxx

Pointing the camera at a person’s face will increase the "person score", while pointing the camera at something other than a person’s face will increase the "no person score".

6. Camera Tutorials

6.1. camera sample application

In this chapter, we will discuss an example using the Spresense Camera board.
This sample is intended to give you a taste of the basic usage of Spresense Camera.

6.1.1. System Requirements

It is assumed that the following hardware is used to run this sample

  • Spresense Main Board

  • Spresense Camera Board

  • Spresense Extension Board

  • Arduino UNO LCD Connector board

  • ILI9341 2.2inch LCD

6.1.2. Source code

The source code for this example can be found under examples/camera.

The structure of the files in the directory looks like this

camera/

.
├── Kconfig
├── Make.defs
├── Makefile
├── README.txt
├── camera_bkgd.c
├── camera_bkgd.h
├── camera_fileutil.c
├── camera_fileutil.h
└── camera_main.c

The main files and folders are outlined below.

file/folder name

camera_main.c

The file that implements the main() function.

camera_bkgd.c

implementation of the utility functions to control NX, the NuttX graphics system.

camera_fileutil.c

The file that implements the utility functions to save the data acquired from the image sensor to a file.

6.1.3. Build procedure

It describes the build procedure using the CLI version, but you can build this sample application in the IDE version as well by choosing the same configuration.

  1. Go to the sdk directory.

    Loading the build-env.sh script enables the Tab completion feature of the configuration tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. Configuration and build.

    Execute the configuration with the argument examples/camera.
    After a successful build, a nuttx.spk file is generated under the sdk folder.

    make distclean
    tools/config.py examples/camera
    make
    
  3. Write nuttx.spk to the Spresense board.

    In this example, the serial port is set to /dev/ttyUSB0 and the write speed baudrate is set to 500000 bps.

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

6.1.4. Operation check

Open a serial terminal and run the camera command.

  1. Start the serial terminal.

    The following is an example using a minicom terminal. The serial port is set to /dev/ttyUSB0 and the baudrate is set to 115200 bps.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Execute the camera command from NuttShell.

    From the nsh> prompt, type camera and press enter to run.

    nsh> camera

    If it works correctly, the LCD will display the camera’s image.
    Note that the default is to display 10 frames and then exit.
    In order to display the camera image continuously, run the following command with 0 in the argument.

    nsh> camera 0

6.2. multiwebcam Sample Application

This chapter shows you how a multiwebcam sample application works. This sample shows how to use the IDY Spresense Wi-Fi Add-on Board iS110B to send a JPEG image taken by the Camera to a connected device via Wi-Fi. There are two modes in this sample.

  1. one-to-one communication, using Motion JPEG over HTTP as the transfer protocol (You can monitor the camera images by accessing Spresense from your browser)

  2. one to many communication mode to retrieve image data from multiple Spresense using a proprietary transfer protocol and display it in a PC app (You’ll need a special app, but you can view multiple camera images on one screen.

In both cases, Spresense acts as a server that sends the images and connects to the server from a browser or a PC tool to retrieve them.

This sample has been implemented based on the following technical elements.

  • Spresense Camera (V4L2 like I/F)

  • multi pthread programing

  • socket programing

  • Tiny HTTP server

6.2.1. Operating Environment

To run this sample, it is assumed to use the following hardware.

  • Spresense Main Board

  • Spresense Camera Board

  • IDY Wi-Fi Add-on Board iS110B

6.2.2. Source Code

The source code for this sample can be found under examples/multi_webcamera.

The structure of the files in the directory is as follows.

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/

The main files and folders summary is as follows.

file/folder name summary

multiwebcam_main.c

Sample code main processing implementation file.

multiwebcam_server.c

Network processing implementation files.

multiwebcam_threads.c

Implementation files for Thread to obtain JPEG data from the image sensor and to send the obtained JPEG data to the connected client.

multiwebcam_util.c

Queue implementation files for sending and receiving messages between Threads.

startup_script/init.rc

Sample (Template) Script for Spresense Launch.

host/

PC-side sample code (Python) in the case of multi-camera mode.

6.2.3. Source Code Description

This section describes the behavior of the source code.

The entire app is shown in the following figure

multiwebcam overview

In this app, three threads, main(), camera_thread() and jpeg_sender(), work together. The following is a description of the behavior of each of these three.

6.2.3.1. main()

Main Functions.
Initialize the Spresense Video driver (including initializing the image sensor) and start camera_thread() as a Thread. (1) in the green block above
Then we create a socket as a server and call the accept() function and wait for a connection from the client. (2) in the green block above
When connected by the client, invoke jpeg_sender() as a Thread to send the JPEG. (3) in the green block above
After invoking jpeg_sender(), in main(), we wait for the jpeg_sender() Thread to exit, and when it does, we accept() again and wait for a connection from a new client. (3) in the green block above

The key steps and source code are excerpted below.

Number in green block file name:Line number Code Description

multiwebcam_main.c:77

video_initialize(VIDEO_DEV_PATH);

Initialize video driver.

multiwebcam_main.c:79

v_fd = open(VIDEO_DEV_PATH, 0);

Open the video driver device file.

multiwebcam_main.c:86

ret = multiwebcam_prepare_camera_buf(v_fd, V4L2_BUF_TYPE_STILL_CAPTURE, V4L2_BUF_MODE_RING, 2, &vbuffs);

Creating a JPEG data buffer and registering it to the Video driver.

multiwebcam_main.c:105

rsock = multiwebcam_initserver(MULTIWEBCAM_PORT_NO /* Port Number */);

Creating the server socket.

multiwebcam_main.c:109

cam_thd = multiwebcam_start_camerathread(v_fd);

Generating camera_thread() Thread.

multiwebcam_main.c:117

wsock = multiwebcam_waitconnection(rsock, &client);

Waiting for a connection from the client, specifically reading the accept() function and waiting for a connection.

multiwebcam_main.c:122

jpeg_thd = multiwebcam_start_jpegsender(wsock);

Generate jpeg_sender() Thread.

multiwebcam_main.c:123

pthread_join(jpeg_thd, NULL);

Waiting for exiting jpeg_sender() thread.

Back to (2) after exiting the thread.

6.2.3.2. camera_thread()

Thread for acquiring images from the image sensor. When invoked by the main() function, issues VIDIOC_DQBUF to the Video driver to retrieve the captured data. (1) in the blue block above.
Then, check if the jpeg_sender() Thread is running, and if so, send the acquired JPEG data to action_queue to pass it to the jpeg_sender() Thread. (2) in the blue block above.
Get an empty buffer from empty_queue() after sending the data.(3) in the blue block above.
Then set an empty buffer in the Video driver with VIDIOC_QBUF and wait for the JPEG data to be acquired from the image sensor. (4) in the blue block above.
Now repeat this.

The key steps and source code are excerpted below.

Number in blue block File name:Line number Code Description

multiwebcam_thread.c:72

multiwebcam_get_picture_buf(v_fd, &buf, V4L2_BUF_TYPE_STILL_CAPTURE);

Get a JPEG image from the Video driver.

multiwebcam_thread.c:78

while (!is_run){ …​ }

Waiting for jpeg_sender() thread wakeup.

multiwebcam_thread.c:93

multiwebcam_push_action(multiwebcam_get_vbuffer(&buf));

Push the read JPEG data to action_queue.

multiwebcam_thread.c:98

while (multiwebcam_is_emptyqueue_empty() && is_run){ …​ }

Wait until the used up buffer is pushed to empty_queue.

multiwebcam_thread.c:98

for (vbuf = multiwebcam_pull_empty(); vbuf != NULL; vbuf = multiwebcam_pull_empty()){ …​ }

All buffers in empty_queue are re-registered in the Video driver.

When you finish re-registering to the Video driver, return to (1).

6.2.3.3. jpeg_sender()

Thread to run to send JPEG data once a connection is established from the client. Once the connection from the client is established, it will be started from the main() Thread and set the is_run variable to true to tell camera_thread() that it has been started.
Then wait for camera_thread() to push the JPEG data to action_queue, and then retrieve it when pushed. (1) in the yellow block above.
Once the data is retrieved, send the JPEG data to the client according to the current selected protocol (Motion JPEG over HTTP, or proprietary transfer protocol). (2) in the yellow block above.
After the transmission is complete, send a buffer of used JPEG data to empty_queue. (3) in yellow block above.
If we lose the connection with the client, we set is_run to false to tell camera_thread() to exit, and then exit the Thread. (Dotted line (4) from the yellow block to the green block in the above figure)

The key steps and source code are excerpted below.

Number in yellow block File name:Line number Code Description

multiwebcam_thread.c:145

is_run = true;

Tell the camera_thread() Thread to start.

multiwebcam_thread.c:149

multiwabcam_sendheader(sock);

Send data that should be sent only once at the beginning of the connection.
Specifically sends the HTTP response header. (Only in the case of Motion JPEG over HTTP)

multiwebcam_thread.c:155

while(multiwebcam_is_actionqueue_empty()){ …​ }

Wait while action_queue is empty (until camera_thread() pushes the JPEG).

multiwebcam_thread.c:159

buf = multiwebcam_pull_action();

Extract the JPEG data from action_queue.

multiwebcam_thread.c:162

ret = multiwebcam_sendframe(sock, (char *)buf→start, (int)buf→jpg_len);

Send the retrieved data to the client.

multiwebcam_thread.c:166

multiwebcam_push_empty(buf);

Push the finished sending buffer to empty_queue.

After pushing it to empty_queue, return to ①.

6.2.4. Build and execution instructions (for one-to-one Motion JPEG over HTTP mode)

This sample code exists in two modes: one mode to send it in one-to-one Motion JPEG over HTTP, and another mode to send it in many-to-one proprietary transfer protocol.
This section describes the case of letting them communicate using one-to-one Motion JPEG over HTTP.
If you want to communicate in many-to-one proprietary transfer protocol mode, see [uild_and_run_instructions_(many-to-one_proprietary_transfer_protocol_mode)].

6.2.4.1. How to build it

This article describes the build procedure using the CLI version, but you can build this sample application in the IDE version as well by choosing the same configuration.

  1. Go to the sdk directory

    Loading the build-env.sh script enables the Tab completion feature of the configuration tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. Configure and build

    Execute the configuration with the argument examples/multiwebcam.
    When the build succeeds, a nuttx.spk file is generated directly under the sdk folder.

    make distclean
    tools/config.py examples/multiwebcam
    make
    
  3. Flash nuttx.spk into Spresense board

    After the build is successfully completed, a file named nuttx.spk will be generated in the sdk folder, which will be written to the target Spresense. In this example, the serial port is set to /dev/ttyUSB0 and the write speed baudrate is set to 500000 bps.

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

To launch the sample app on the device side, open a serial terminal, connect to Wi-Fi and run the multiwebcam command.

  1. Launch serial terminal

    First, from the terminal software, enter NuttShell by connecting to the Spresense serial port.
    In the example below, the serial port connected to the target Spresense is /dev/ttyUSB0, connected with minicom with 115200 bps as baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Connect to Wi-Fi network

    In the example below, we boot up Wi-Fi in AP mode, with SSID as presence_net and password as 0123456789.
    Once the Wi-Fi module has been successfully launched, it will set an IP address and check it. In the example below, there is about 5 seconds of sleep before the Wi-Fi module finishes booting.
    In the ifconfig command, the IP address will be assigned, as shown below.

    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

    In the above example, 192.168.11.1 would be the IP address of Spresense.
    Note that HWaddr may vary depending on the purchased Wi-Fi module.

  3. Launch the application

    Once the Wi-Fi setup is complete, launch the multiwebcam app.

    nsh> multiwebcam

    Now, the Spresense side is ready.

  4. Connect to the Wi-Fi network you have created with your PC or smartphone

    Next, in order to view the images from the camera in a browser, such as a PC or smartphone, first connect your PC or smartphone to the Wi-Fi network you just activated with Spresense. Connect to the Wi-Fi network with your phone or other device.
    The SSID of the Wi-Fi you are connecting to will be spresense_net, which you set up earlier. In the Wi-Fi connection settings on your PC or phone, look for spresense_net and connect.
    Choose WPA/WPA2 as the encryption method when connecting.
    The password is 0123456789, set above.

  5. Connect to the Spresense camera in the browser to view Live View

    Once you have successfully connected to the Wi-Fi network, you can open a browser on the connected PC or smartphone and enter the following URL in the URL input field to view the camera’s image.

    http://192.168.11.1

    If it works correctly, the image of the camera will be displayed on the browser.

6.2.5. Build and run instructions (many-to-one proprietary transfer protocol mode)

Then describes how to build and run it using a many-to-one proprietary protocol.
Basically, the build method is the same as Motion JPEG over HTTP.
The only difference in the build method is that in the Config menu of the Example, disable the "Http MJPEG is used" option.
The usage on the device side is almost the same, with the only difference being that the WiFi connection settings are stations instead of access points.
Now, let’s take a step-by-step description.

6.2.5.1. How to build

This section describes the build procedure using the CLI version, but you can build this sample application in the IDE version as well by choosing the same configuration.

  1. Go to the sdk directory.

    Loading the build-env.sh script enables the Tab completion feature of the configuration tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. Configuration.

    Execute the configuration with the argument examples/multiwebcam.

    make distclean
    tools/config.py examples/multiwebcam
    

    Then open the menu config with the following command.

    make menuconfig
    

    After opening the menu, use the arrow keys to go to "Application Configuration" at the bottom of the menu and press Enter to enter the menu and then proceed to "Examples"

      "Application Configuration"
           -> "Spresense SDK"
                 -> "Examples"

    Once you get into Examples "[\brain] Multi Web Camera" Among the items checked with "[*] Http MJPEG is used" Hover over this item and uncheck it with the spacebar.

    multiwebcam config

    Once unchecked, hover over "<Exit>" and press enter, and then do <Exit> at the top level, you will be asked to "Do you wish to save your new configuration? Exit menuconfig.

  3. Build.

    Once the configuration is complete, build with the following command.

    make
    

    After a successful build, a nuttx.spk file is generated under the sdk folder.

  4. Write the nuttx.spk to the Spresense board, which should be written to the nuttx.spk file.

    After the build is successfully completed, a file named nuttx.spk will be generated in the sdk folder, which will be written to the target Spresense. In this example, the serial port is set to /dev/ttyUSB0 and the write speed baudrate is set to 500000 bps.

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

To launch the sample app on the production side, open a serial terminal, connect to Wi-Fi and run the multiwebcam command.

  1. Launch the serial terminal.

    First, from the terminal software, enter NuttShell by connecting to the Spresense serial port.
    In the example below, the serial port connected to the target Spresense is /dev/ttyUSB0 with minicom with the baudrate set to 115200 bps.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Wi-Fi Connection.

    In the example below, we will launch Wi-Fi in STA mode, so please prepare the SSID and password of the Wi-Fi network you want to connect to beforehand, because in STA mode, you will be connecting to an existing Wi-Fi network.
    In this example, the SSID is described as hogehoge and the password as hogehoge1.
    Once the Wi-Fi module has been successfully launched, it will set an IP address and check it. In the example below, there is about 5 seconds of sleep before the Wi-Fi module finishes booting.
    In the ifconfig command, the IP address will be assigned as follows.

    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.XXX DRaddr:XXX.XXX.XXX.XXX Mask:255.255.255.0

    As a result of ifconfig, you will see the resulting IP address connected to inet addr.
    Note that HWaddr may vary depending on the purchased Wi-Fi module.
    Please make a note of the IP address you get here as it will be needed in the PC app settings.

  3. Launch the app.

    Once the Wi-Fi setup is complete, launch the multiwebcam app.

    nsh> multiwebcam

    Now, the Spresense side is ready.

    If you are using more than one Spresense, connect to Wi-Fi and launch the multiwebcamera in the same way.

  4. Launching the PC app

    The PC-side app was created in Python as a reference to display images from multiple cameras. The app has been verified to work on a Linux PC.

    To run, you must first install Python 2.7.
    In addition, you will need the following libraries of Python, please install each of the following.

    • python-wxgtk3.0

    • python-wxtools

    In Linux, you can install with the following command. The following commands can be installed on Linux

    sudo apt install python-wxgtk3.0 python-wxtools
    

    After installing the necessary Python libraries, the first step is to set the IP address of the Spresense to connect to. We will use the IP address that we wrote down when we launched the actual device earlier.

    Go into the host/ folder and open MultiCameraFrame.py.
    listed in the 183rd line of that file,

            servers = ( ('192.168.11.1', 10080),
                        ('192.168.11.2', 10080),
                        ('192.168.11.3', 10080),
                        ('192.168.11.4', 10080) )

    Replace the IP address listed as 192.168.11.1 ~ 4 in the code "192.168.11.1 ~ 4" with the IP address you wrote down.
    Up to four devices can be displayed, but if you don’t have four, just change it for the number of devices you have and leave the rest as they are.
    As an example, if there are two devices, 10.0.0.0.5 and 10.0.0.0.8, respectively, you would configure them as follows.

            servers = ( ('10.0.0.5', 10080),
                        ('10.0.0.8', 10080),
                        ('192.168.11.3', 10080),
                        ('192.168.11.4', 10080) )

    When you are done editing, you can save the file and launch the app by hitting the following command from the command prompt.

    python MultiCameraFrame.py
    

    If launched correctly, the Window will appear on the full screen and begin to display images from the device.

    Naturally, the PC must be connected to the same Wi-Fi network as the one to which the device is connected.

6.2.5.3. Description of the PC side app

Here is a brief description of the PC app.
First, the structure of the PC app is shown in the following figure

multiwebcam pcapp

The host app utilizes wxPython as the Window programming framework to generate MultiCamFrame with a custom panel implemented in MultiCameraFrame.py: WebCamPanel.
Pass a list of IP addresses and port numbers of the Spresense to connect to its arguments.
When Frame starts, it generates a WebCamPanel, in which it generates four StaticBitmaps and pastes them into the frame, and then generates four NetImgReceivers based on the list of IP addresses and port numbers that are passed to it. Implemented in NetImgReceiver.py, it gives the IP address and port number of the server to connect to the ID number associated with the StaticBitmap.
After the NetImgReceiver is created, receiveThread() is launched as a Thread and begins parallel operations.
receiveThread() makes a connection request to the server on the Spresense device side based on the specified server IP and port number.
Once the connection is established, start receiving JPEG images.
When the reception of JPEGs is complete, it fires WebCamPanel’s PictUpdateEvent and sends the received JPEG data to WebCamPanel with the ID number.
In WebCamPanel, onPictUpdate() is called back when a PictUpdateEvent is fired. This function receives the received JPEG data and ID number, adjusts the image size and then displays the received image in StaticBitmap based on the ID number.

6.2.6. Appendix : Packet format

Here we explain the format of the packets exchanged in each mode.

6.2.6.1. Motion JPEG over HTTP

It uses "multipart/x-mixed-replace", which is defined as an HTML standard. See below for details.
https://html.spec.whatwg.org/#read-multipart-x-mixed-replace

6.2.6.2. Proprietary Protocols

Proprietary protocol will be a protocol created for the simple purpose of just sending JPEG data to a connected client.
The packets are ordered as delimiters, starting with the four-character Ascii code "SZ: ", followed by the JPEG data size (number of bytes) of 4 bytes (Little endian), and then the JPEG data for that size.
The above format can be illustrated as follows.

multiwebcam pktfmt

6.2.7. Tips : Auto start of spresense applications.

Commands running at the presense nsh prompt can also be executed automatically at startup.
See How to start applications automatically for more information.

7. Filesystem Tutorials

7.1. SmartFS

This section describes the SmartFS used for the SPI-Flash filesystem.

Please refer to the following NuttX Wiki for more information of the SmartFS filesystem.

7.1.1. Initializing & mount

The board_flash_initialize() function is called from cxd56_bringup() during boot-up.

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

In the board_flash_initialize() function, the SPI-Flash is initialized at the SmartFS filesystem and is mounted on the /mnt/spif directory.

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 Initialize the SPI-Flash to Memory Technology Device (MTD).
2 Initialize the MTD as the SmartFS filesystem.
3 Mount /dev/smart0d1 device file on /mnt/spif directory.

7.1.2. Configuration

There are several configurations for using the SmartFS.

The following table shows the SDK default configuration (sdk/configs/default/defconfig).

Configuration Value Description

CONFIG_FS_SMARTFS

y

Enable the SmartFS filesystem.

CONFIG_SMARTFS_ERASEDSTATE=0xff

0xff

It means erased state when SPI-Flash value is 0xFF.

CONFIG_SMARTFS_MAXNAMLEN

30

Max length of filename is 30.

CONFIG_SMARTFS_MULTI_ROOT_DIRS

y

Supports for multi root directory, but uses a root directory in the default configuration.

CONFIG_MTD_SMART

y

Use the SmartFS as the MTD.

CONFIG_MTD_SMART_SECTOR_SIZE

4096

The size of a sector is 4096 byte. Even a small file uses 4096 bytes (1 sector). Reducing this size will increase the number of files to be managed, but if you make it too small, the search process for the file will take a longer time.

CONFIG_MTD_SMART_WEAR_LEVEL

n

If this is enabled, the file is periodically moved to a new sector as the sector rotation. In other words, the read-only files may be deleted by powered off during sector rotation. In case of power failure, it is recommended to disable this option.

CONFIG_MTD_SMART_ENABLE_CRC

y

Use the CRC check for detecting bad sector.

CONFIG_MTD_SMART_FSCK

y

The filesystem may be inconsistent by a power loss during a file operation. If this is enabled, it checks the filesystem at startup, and invalid files are deleted to preserve the integrity of the filesystem.

CONFIG_MTD_SMART_MINIMIZE_RAM

n

This uses a cached table of logical-physical sector. If enabled, the performance of the filesystem is degraded. It is disabled by default.

7.1.3. Operation check

You can check the filesystem by the df command on the NuttShell prompt.

nsh> df -h
  Filesystem    Size      Used  Available Mounted on
  smartfs         4M       68K      4028K /mnt/spif
  procfs          0B        0B         0B /proc

Make sure that /mnt/spif is mounted as a smartfs filesystem, and the total size is 4MByte, and you can check the used size and available size.

Create a new file.

nsh> echo "This is a test file." > /mnt/spif/test.txt

Check size of the file.

nsh> ls -l /mnt/spif
/mnt/spif:
 -rw-rw-rw-      21 test.txt

Read the file.

nsh> cat /mnt/spif/test.txt
This is a test file.

If you want to operate the file by programming, you can use low-level input/output functions such as open/read/write/close, or file input/output functions such as fopen/fread/fwrite/fclose.

7.1.4. How to format

The SPI-Flash is initialized for SmartFS filesystem by default. If you want to clean up the SPI-Flash, you can use the mksmartfs command.

Usage
nsh> help mksmartfs
mksmartfs usage:  mksmartfs [-s <sector-size>] [-f] <path> [<num-root-directories>]
Example
nsh> mksmartfs -s 4096 -f /dev/smart0d1 1

After mksmartfs command, a few sectors are reserved for the root directory area and the remainder is available for user.

nsh> df -h
  Filesystem    Size      Used  Available Mounted on
  smartfs         4M       28K      4068K /mnt/spif
  procfs          0B        0B         0B /proc

7.1.5. How to erase flash

If you cannot find a /dev/smart0d1 device file at startup, or if you want to erase the SPI-Flash area used by your application, you can use the flash_eraseall command.

To use the flash_eraseall command, enable CONFIG_SYSTEM_FLASH_ERASEALL in SDK configuration.

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

If you want to use the board as SmartFS after erasing the SPI-Flash, please reboot the board and then refer to the above-mentioned format procedure.

7.2. FAT file system

This section describes the FAT file system used for the SD card.

7.2.1. Initializing & mount

The board_sdcard_initialize() function is called from cxd56_bringup() during boot-up.

File: nuttx/boards/arm/cxd56xx/spresense/src/cxd56_bringup.c

#ifdef CONFIG_CXD56_SDIO
  ret = board_sdcard_initialize();
  if (ret < 0)
    {
      _err("ERROR: Failed to initialize sdhci. \n");
    }
#endif

In the board_sdcard_initialize() function, set the GPIO interrupt to detect the insertion and removal of the SD card.

If the SD card is inserted, the board_sdcard_enable() function is called to initialize the SD Host Controller and mount the SD card to the /mnt/sd0 directory. If the SD card is removed, the board_sdcard_disable() function will be called to terminate the SD host controller and unmount /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 Set the GPIO interrupt for SD card insertion/ejection detection.
2 Initialize the SD Host Controller.
3 Mount /dev/mmcsd0 device to /mnt/sd0.
4 Unmount /mnt/sd0.
5 Finalize the SD Host Controller.

7.2.2. Configuration

There are several configurations for using the FAT file system.

The following table shows the SDK default configuration (sdk/configs/default/defconfig).

Configuration Value Description

CONFIG_CXD56_SDIO

y

Enable SD card.

CONFIG_FS_FAT

y

Enable FAT file system.

CONFIG_FAT_LCNAMES

y

Enable the upper/lower case of file name.

CONFIG_FAT_LFN

y

Enable long file names.

CONFIG_FAT_MAXFNAME

64

Define maximum long file name.

CONFIG_FS_FATTIME

y

Enable FAT date and time (timestamp). When the RTC is set to the current time (January 1, 1980 or later), the creation and modification dates of the file will be recorded.

7.2.3. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    tools/config.py device/sdcard
    make
    

    If the build is successful, a nuttx.spk file will be created under the sdk directory.

  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

7.2.4. Operation check

You can check the filesystem by the df command on the NuttShell prompt.

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

Make sure that /mnt/sd0 is mounted as a vfat filesystem, and you can see the total (Size), used size (Used) and available size (Available).

Set RTC date and time to use the timestamp of file.

nsh> date -s "Feb 22 12:34:00 2021"
nsh> date
Feb 22 12:34:02 2021

Create a new file.

nsh> echo "This is a test file." > /mnt/sd0/test.txt

Check size of the file.

nsh> ls -l /mnt/sd0/test.txt
 -rw-rw-rw-      21 /mnt/sd0/test.txt

Read the file.

nsh> cat /mnt/sd0/test.txt
This is a test file.

If you open the properties of the file in the SD card from your PC, you can confirm that the file timestamp is supported correctly.

If you want to operate the file by programming, you can use low-level input/output functions such as open/read/write/close, or file input/output functions such as fopen/fread/fwrite/fclose.

7.2.5. How to format

On your PC, use an SD memory card formatter to format the FAT file system.

exFAT file system is not supported.

If you want to format the SD card on the board, you can use the mkfatfs command.

Usage
nsh> help mkfatfs
mkfatfs usage:  mkfatfs [-F <fatsize>] [-r <rootdirentries>] <block-driver>

The procedure to format FAT32 is shown below.

Example
nsh> mkfatfs -F 32 /dev/mmcsd0

8. HostIF Tutorials

8.1. HostIF example application

This chapter shows the usage of HostIF (Host Interface) example application.

This example application uses two Spresense boards. One is for the HostIF device, and another is for the Host. This can support the HostIF communication via I2C or SPI by connecting between Spresense boards. The example code also includes the implementation for the host.

8.1.1. Requirements

I2C connection diagram

Connect the I2C(#3) terminals on a B2B breakout board to the I2C(#0) terminals on a main board as the host.
The I/O voltage is 1.8V.

tutorial hostif i2c
B2B breakout Host

No.

PAD name

PAD name

I/O voltage

77

I2C3_BCK

I2C0_SCL

1.8V

71

I2C3_BDT

I2C0_SDA

1.8V

SPI connection diagram

Connect the SPI(#2) terminals on a B2B breakout board to the SPI(#5) terminals on a main board as the host.
The I/O voltage is 1.8V.

tutorial hostif spi
B2B breakout Host

No.

PAD name

PAD name

I/O voltage

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

8.1.2. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Depending on the connection configuration, either I2C or SPI, execute the appropriate configuration.
    If the build is successful, a nuttx.spk file will be created under the sdk directory.

    For I2C connection,

    tools/config.py examples/hostif_i2c
    make
    

    For SPI connection,

    tools/config.py examples/hostif_spi
    make
    
  3. Flashing nuttx.spk into both Spresense boards

    In this case, the serial port of a HostIF board is connected to /dev/ttyUSB0 and the one of a Host board is connected to /dev/ttyUSB1. The serial port number will change depending on the user environment.
    The baudrate for uploading speed is set to 500000 bps. If it fails to upload, change the baudrate to 115200 bps.

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

8.1.3. Operation check

Open the serial terminals of both Spresense boards.

  1. Open the serial terminal

    This is an example of using a minicom terminal The serial port of a HostIF board is connected to /dev/ttyUSB0 and the one of a Host board is connected to /dev/ttyUSB1. The serial port number will change depending on the user environment. For both boards, set the baudrate to 115200 bps.

    minicom -D /dev/ttyUSB0 -b 115200
    minicom -D /dev/ttyUSB1 -b 115200
    
  2. Run hostif command from NuttShell on the HostIF board.

    After initializing the HostIF driver,
    write the build version to the HostIF communication buffer,
    start a thread that writes a timestamp periodically to the HostIF communication buffer,
    and start a thread that receives the data sent from the host and sends it back to the 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. Run host_i2c or host_spi command from NuttShell on the host board.

    Receive the build version via I2C or SPI and output to the console,
    send the data to the hostif board, receive the loop-backed data and output to the console.
    Finally, receive the timestamps and output to the console.

    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)

8.1.4. Explanation of example code

This section explains the configuration of the communication buffer and the data contents in this example.

For detailed specification of HostIF, refer to SDK developer guide.

8.1.4.1. The configuration of the communication buffer.

Create a communication buffer to be sent from Host to Spresense (Index=0) and 3 communication buffers to be sent from Spresense to Host (Index=1,2,3).

diag b95e2f8db8e1ace42b908a2fec4a311c

The purpose of each communication buffer is as below.

Index Size Direction Device filename Description

0

0x100

Read

/dev/hostifr0

For loopback reception (general-purpose communication)

1

0x100

Write

/dev/hostifw1

For loopback transmission (for general-purpose communication)

2

0x029

Write

/dev/hostifw2

For sending the build version (for fixed data)

3

0x008

Write

/dev/hostifw3

For sending timestamps (for updated data on real-time)

An example code for the buffer configuration is shown below.

#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
    },
};
8.1.4.2. HostIF operation

See the following link for example code on the HostIF side.

The processing flow of the example code is shown below.

  1. Initialize the hostif driver with the specified buffer configuration.

    #ifdef CONFIG_EXAMPLES_HOSTIF_I2C
      ret = hostif_i2cinitialize(&conf);
    #else
      ret = hostif_spiinitialize(&conf);
    #endif
  2. Start a hostif_updater thread.

    Write the build version obtained by uname() to the Buffer(Index=2). It assume that the HostIF writes the information only once and then the host refers to it as fixed read-only data.

      /* Write-once the constant software version to BUFFER2 */
    
      struct utsname name;
    
      uname(&name);
    
      ret = write(wfd2, &name.version, VERSION_NAMELEN);

    Write the timestamp obtained by clock_gettime() to Buffer(Index=3) every a second. It assumes that the HostIF periodically overwrites and updates the data, and the host always gets the latest data by reading asynchronously.

      struct timespec ts;
    
      clock_gettime(CLOCK_REALTIME, &ts);
    
      ret = write(wfd3, &ts, sizeof(ts));
  3. Start a hostif_loopback thread.

    Receives the data sent from the Host via Buffer(Index=0), loops back and sends it to the host via Buffer(Index=1).

      /* blocking read */
    
      size = read(rfd, buffer, sizeof(buffer));
    
      /* blocking write */
    
      size = write(wfd, buffer, size);
8.1.4.3. Host operation

See the following links for example code on the Host side.

The processing flow of the example code is shown below.

  1. Open the I2C or SPI driver.

      /* Open the i2c driver */
    
      fd = open("/dev/i2c0", O_WRONLY);
      /* Open the spi driver */
    
      fd = open("/dev/spi5", O_WRONLY);
  2. Get the build version from Buffer(Index=2).

      /* Get the version information from slave */
    
      get_version(fd);

    In order for the host to be able to refer the fixed read-only data, the host set a lock-flag of the buffer on the communication protocol. By keeping the lock of the buffer, the HostIF is prohibited from updating, and the host is always readable.

  3. The host sends the data to Buffer(Index=0) and receives the loop-backed data from Buffer(Index=1).

    Repeat this behavior 10 times.

      /* 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. Get the timestamp from Buffer(Index=3).

    Repeat this behavior 10 times every a second.

      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. Finally, close the I2C or SPI driver.

      /* Close the i2c or spi driver */
    
      close(fd);

9. System tools

Spresense SDK provides the system tools on NuttShell.

Category System tool Description

LOG

setlogmask

A utility tool to change the log level dynamically

logdump

A utility tool to dump the logging information on Backup SRAM

logsave

A utility tool to save the logging information on Backup SRAM into SPI-Flash

GPIO

gpio

A utility tool to get/set GPIO/pin settings

gpioint

A utility tool for GPIO interrupt

I2C

i2c

A utility tool to communicate with I2C device

PMIC

pmic

A utility tool to control PMIC(PowerManagement IC)

USB

usbmsc

A utility tool for USB MSC (Mass Storage Class)

cdcacm

A utility tool for USB CDC/ACM

Zmodem

zmodem

A utility tool for Zmodem transfer

Stack

stackmonitor

A utility tool to monitor stack usages of all tasks and threads

10. GPIO utility tool

This section describes the GPIO utility tool.
By using this tool, it’s possible to check the status of each pin and set the inputs and outputs to the GPIO pins.

10.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    tools/config.py feature/gpiotool
    make
    

    If the build is successful, a nuttx.spk file will be created under the sdk directory.

  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

10.2. Operation check

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type gpio command on NuttShell prompt. The usage is below.

    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 command can show the status of the specified pin.
    gpio conf command can configure the setting of the specified pin.
    gpio read command can read a value from the specified pin.
    gpio write command can write a value to the specified pin.

10.2.1. gpio stat command

gpio stat shows the status of all digital pins.
When the argument is specified, it shows the status from <from_pin> to <end_pin>.

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

Pin function mode.
Mode 0 is used for GPIO function, and Mode 1~3 are used for functions other than GPIO, such as I2C and UART.
All the pins are divided into several groups, and the mode is set for each group.
For more information, refer to the SDK development guide Pin specification.

I/O

Input means whether the input of pin is enabled or not.
Output means whether the output of GPIO pin is enabled or not.

mA

Drive current (2mA or 4mA).

Pull

--:Float, PU:Pull-Up, PD:Pull-Down, BK:Bus-Keeper

Read

A value read from the pin.

IRQ

If the GPIO interrupt is set, it means the IRQ number.

Type

If the GPIO interrupt is set, it means the interrupt polarity.
(High:Level, Low:Level, Rise:Rising Edge, Fall:Falling Edge, Both:Both Edge)

NF

If the GPIO interrupt is set, it means enable or disable of the noise filter to prevent chattering.

EN

If the GPIO interrupt is set, it means enable or disable of the interrupt.

10.2.2. gpio conf command

The gpio conf command can alter the function modes and input/output settings for the specified pin number.
If type gpio conf 37 -m 0 -i -p 2, PIN_SEN_IRQ_IN (No. 37) pin is set to the GPIO function mode, input-enabled, and pull-down.

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

10.2.3. gpio read command

gpio read command reads a value from the port of the specified pin number.
If type gpio read 37, then PIN_SEN_IRQ_IN (No.37) pin is read and the result displays 0 (LOW) or 1 (HIGH).

nsh> gpio read 37
0
nsh> gpio read 37
1

10.2.4. gpio write command

gpio write command writes 0 (LOW), 1 (HIGH) or -1 (HiZ) to the port of the specified pin number.
PIN_I2S1_BCK(No.97) pin is connected to LED0 on the main board. If type gpio write 97 1, it turns LED0 on. If type gpio write 97 0, it turns LED0 off.

nsh> gpio write 97 1
nsh> gpio write 97 0

10.3. Programming

10.3.1. GPIO API

See API Reference Manual for the details of the GPIO API.

To use the GPIO API, include the following files.

#include <arch/board/board.h>
#include <arch/chip/pin.h>

The prototype declarations of the GPIO API are defined:
nuttx/boards/arm/cxd56xx/spresense/include/cxd56_gpioif.h

The pin names used by the GPIO API are defined:
nuttx/arch/arm/include/cxd56xx/pin.h

10.3.2. GPIO input setting

This section shows how to input from the GPIO pin.

  /* Input pin settings */

  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)

  /* Input pin */

  int status = board_gpio_read(PIN_XXX);                     (5)
1 PIN_XXX is input-enabled.
2 PIN_XXX is input-enabled with pull-up.
3 PIN_XXX is input-enabled with pull-down.
4 PIN_XXX is input-enabled with bus-keeper.
5 Read a value from PIN_XXX.

10.3.3. GPIO output setting

This section shows how to output to the GPIO pin.

  /* Output pin setting */

  board_gpio_config(PIN_XXX, 0, false, true,  PIN_FLOAT); (1)
  board_gpio_config(PIN_XXX, 0, false, false, PIN_FLOAT); (2)

  /* Output pin */

  board_gpio_write(PIN_XXX, 0);   (3)
  board_gpio_write(PIN_XXX, 1);   (4)
  board_gpio_write(PIN_XXX, -1);  (5)
1 PIN_XXX is input-disabled. The drive current of the pin is set to 4mA.
2 PIN_XXX is input-disabled. The drive current of the pin is set to 2mA.
3 Write LOW to PIN_XXX.
4 Write HIGH to PIN_XXX.
5 Disable the output of PIN_XXX.

10.3.4. GPIO interrupt setting

This section shows how to use the GPIO interrupt.

static int gpio_handler(int irq, FAR void *context, FAR void *arg)
{
  /* Interrupt handler */
}

  /* Interrupt setting */

  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 The interrupt polarity of PIN_XXX is set to HIGH level. The interrupt handler is registered.
2 The interrupt polarity of PIN_XXX is set to LOW level. The interrupt handler is registered.
3 The interrupt polarity of PIN_XXX is set to rising edge. The interrupt handler is registered. The Noise filter is enabled.
4 The interrupt polarity of PIN_XXX is set to falling edge. The interrupt handler is registered. The Noise filter is enabled.
5 The interrupt polarity of PIN_XXX is set to both edge. The interrupt handler is registered. The Noise filter is enabled.
6 Disable the interrupt of PIN_XXX.
7 Enable the interrupt of PIN_XXX.

11. PMIC utility tool

This section describes the PMIC utility tool.
By using this tool, it’s possible to set the load-switch and GPOs on PMIC(CXD5247).

11.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    tools/config.py feature/pmictool
    make
    

    If the build is successful, a nuttx.spk file will be created under the sdk directory.

  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

11.2. Operation check

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type pmic command on NuttShell prompt. The usage is below.

    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 shows the list of status.

    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

    If type pmic -e GPO0, then set enable to GPO0.
    If type pmic -d GPO0, then set disable to GPO0.
    If type pmic -z GPO0, then set Hi-Z to GPO0.

11.3. Connection

Load switches (LSW) and GPOs are mainly used for various power controls.
It shows a list of connections on the main board or extension board.

DDC_CORE, DDC_IO, and LDO_ANA are connected to CORE, IO, and analog power supply of the processor, respectively. These should not be disabled. The description of LDO_PERI is omitted because it is not connected to the board.
Target Voltage Main board Extension board LTE extension board

LDO_EMMC

3.3V

pin socket 3.3V
(GNSS external antenna)

-

-

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

-

-

-

11.4. Programming

11.4.1. PMIC API

To use the PMIC API, include the following files.

#include <arch/board/board.h>

The prototype declarations of the PMIC API are defined:
nuttx/boards/arm/cxd56xx/spresense/include/cxd56_power.h

The target names used by the PMIC API are defined in the board-dependent files with aliases such as POWER_LTE.
nuttx/boards/arm/cxd56xx/spresense/include/board.h

11.4.2. GPO output setting

This section shows how to output to the GPO pin.
To turn it on or off, you can use either board_power_control() or board_power_control_tristate().
Use the board_power_control_tristate() function if you want to set it to HiZ.

  /* GPO output */

  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 Enable GPO0
2 Disable GPO0
3 Enable GPO0
4 Disable GPO0
5 Set Hi-Z to GPO0

11.4.3. GPO monitor

This section shows how to monitor from the GPO pin.
To read 2-state (on or off), you can use either board_power_monitor() or board_power_monitor_tristate().
Use the board_power_monitor_tristate() function if you want to read 3-state (on, off, or HiZ).

  /* GPO monitor */

  bool bstate = board_power_monitor(PMIC_GPO(0)); (1)

  int istate = board_power_monitor_tristate(PMIC_GPO(0)); (2)
1 Read GPO0. If the result is true, it means Enable. Otherwise false, it means Disable.
2 Read GPO0. The result is 1 for Enable, 0 for Disable, -1 for Hi-Z.

12. USB MSC system tool

This section describes the utility tool to use USB MSC (Mass Storage Class) feature.
When the USB MSC function is enabled, the Host PC can directly access the SD card on the Spresense board.

12.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Since the SD card is mounted as a USB MSC, open the menuconfig with the SD card function enabled.

    tools/config.py feature/usbmsc
    make
    

    If the build is successful, a nuttx.spk file will be created under the sdk directory.

  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

12.2. Operation check

With the SD card inserted in the extension board, connect the USB connector of the extension board to the Host PC with a USB cable.

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type msconn command on NuttShell prompt

    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

    The new removable disk is recognized by the Host PC, and the contents of the SD card on the extension board can be accessed from the Host PC.

  3. To terminate the USB MSC function, type msdis command on NuttShell prompt.

    nsh> msdis
    msdis: Disconnected

13. USB CDC/ACM system tool

This section describes the utility tool to use USB CDC/ACM feature.
When the USB CDC/ACM function is enabled, the USB of the extension board can be used as a serial port.

13.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Open the menuconfig.

    tools/config.py feature/usbcdcacm
    make
    

    If the build is successful, a nuttx.spk file will be created under the sdk directory.

  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

13.2. Operation check

Connect the USB connector of the extension board to the Host PC with a USB cable.

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Type sercon command on NuttShell prompt

    nsh> sercon
    sercon: Registering CDC/ACM serial driver
    sercon: Successfully registered the CDC/ACM serial driver

    The new device file /dev/ttyACM0 is generated on the Spresense board, and a new COM port can be found on the Host PC. You can use this port for serial communication via USB.

  3. To terminate the USB CDC/ACM function, type serdis command on NuttShell prompt.

    nsh> serdis
    serdis: Disconnected

14. Zmodem file transfer

This section describes how to send and receive files between Host PC and Spresense board using Zmodem transfer.

14.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Execute the configuration by specifying feature/zmodem as an argument of config.py.
    If the build is successful, a nuttx.spk file will be created under the sdk directory.

    tools/config.py feature/zmodem
    make
    
  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

14.2. Operation check

Use a serial terminal that supports the Zmodem transfer.

This is using minicom terminal for a example.
If minicom and lrzsz are not installed, install them in advance.

sudo apt install minicom lrzsz

Open a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

minicom -D /dev/ttyUSB0 -b 115200

You can use Zmodem’s rz (receive) and sz (send) commands on NuttShell prompt.

The usage of rz command
tutorial zmodem1 rz
The usage of sz command
tutorial zmodem1 sz

14.2.1. File transfer from Host PC to Spresense board

How to transfer files from the HostPC to the Spresense board is shown below.

  1. On minicom terminal, type CTRL-a and z key, and open the menu (This shortcut key assignment can be changed by the user. See the minicom manual for details.)
    Then press the s key and select Send files.

    tutorial zmodem2 menu
  2. Select zmodem with cursor key and execute with Enter key.

    tutorial zmodem2 upload
  3. Move the folder with the cursor key and the space key, and select the file you want to transfer.
    Select a folder with the cursor keys and press the space key twice to move to the folder.
    Select the file with the cursor keys and the space key, and press the Enter key to start the transfer.

    tutorial zmodem2 dir

    Alternatively, you can press the Enter key and input the file name to execute the transfer.

    tutorial zmodem2 file
  4. The file transfer starts and the transfer is completed when "Transfer complete" is displayed.

    tutorial zmodem2 complete
  5. The transfer destination of the file on the Spresense board can be changed with CONFIG_SYSTEM_ZMODEM_MOUNTPOINT.
    In the default configuration, files are transferred to Flash of /mnt/spif.

If you use TeraTerm terminal, you can send the selected file from File→ Transfer→ ZMODEM→ Send from TeraTerm menu.

14.2.2. File transfer from Spresense board to Host PC

How to transfer files from the Spresense board to the HostPC is shown below.

  1. On NuttShell prompt, specify the file you want to transfer to the argument of the sz command.
    Enter the full path name starting with /.
    The following example uses the -x 1 binary transfer option.

    nsh> sz -x 1 /mnt/spif/test00.dat
  2. The file transfer starts and the transfer is completed when "Transfer complete" is displayed.

    tutorial zmodem3 complete
  3. The file is transferred into the folder where minicom was executed on the Host PC.

If you use TeraTerm terminal, you can receive the file from File→ Transfer→ ZMODEM→ Receive of TeraTerm menu.

15. How to start applications automatically

This section describes how to start the user application automatically instead of the NuttShell prompt.

15.1. Start-up script

By using a start-up script, the system can run according to the script and the application program can be automatically executed when the power is turned on. The startup script can support NuttShell commands with the user application, and the control syntax such as if-then-else-fi or while-do-done.
See NuttShell (NSH) documentation for details.

15.1.1. Usage of start-up script

This is an example to start the examples/hello application automatically.
(The explanation about the configuration and build of NuttX Kernel is omitted.)

  1. Open the menuconfig with the hello application enabled.

    ./tools/config.py -m examples/hello
    
  2. Enable Support ROMFS start-up script

    System tools --> NSH Library ---> Scripting Support
    tutorial autostart menuconfig1
  3. Select Custom ROMFS header path in ROMFS header location and
    input ../../nsh_romfsimg.h in Custom ROMFS header file path and save the configuration.

    Specify the location of nsh_romfsimg.h with a relative path from the sdk/system/nshlib directory.
    tutorial autostart menuconfig2
  4. Next, create a startup script named rcS.template under the sdk directory,
    and describe echo command and hello command as below.

    cat spresense/sdk/rcS.template
    echo Start-up hello application
    hello
    
  5. By using a mkromfsimg.sh tool, generate nsh_romfsimg.h from rcS.template.

    cd spresense/sdk
    ./tools/mkromfsimg.sh .
    ls nsh_romfsimg.h
    nsh_romfsimg.h
    
  6. Build and flash the program as usual.

    make
    tools/flash.sh -c /dev/ttyUSB0 nuttx.spk
    
  7. When you turn on the power of the board, you can confirm that the hello application described in the script has been started before NuttShell prompt is displayed.

    tutorial autostart log

15.1.2. Change script location

  1. Refer to the following procedure if you’d like to change the location of rcS.template.

    cat spresense/examples/hello/rcS.template
    echo Start-up hello application
    hello
    
  2. Change the directory where rcS.template is located and generate nsh_romfsimg.h by using mkromfsimg.sh tool.

    cd spresense/examples/hello
    ../../sdk/tools/mkromfsimg.sh ../../sdk
    ls nsh_romfsimg.h
    nsh_romfsimg.h
    
  3. Open configuration menu, and input ../../../examples/hello/nsh_romfsimg.h into Custom ROMFS header file path.

    Specify the location of nsh_romfsimg.h with a relative path from the sdk/system/nshlib directory.
  4. The rest of the build procedure is the same as before.

15.2. SDK Entrypoint

There is another way that uses CONFIG_SDK_USER_ENTRYPOINT than the startup script. The default configuration of CONFIG_SDK_USER_ENTRYPOINT is nsh_main, but if you change it to hello_main, then the hello application would be launched on startup.

tutorial autostart entrypoint
The application needs to be initialized at startup. Add #include <sys/boardctl.h> and boardctl(BOARDIOC_INIT, 0) into the user entrypoint function.
#include <sys/boardctl.h>

#ifdef CONFIG_BUILD_KERNEL
int main(int argc, FAR char *argv[])
#else
int hello_main(int argc, char *argv[])
#endif
{
  /* Initialize apllication */
  boardctl(BOARDIOC_INIT, 0);

  printf("Hello, World!!\n");
  return 0;
}

16. Loadable ELF tutorial

Loadable ELF is the ability to create the OS and application in separate binaries and load and run the application dynamically.

When an application is created as a loadable ELF, it can be loaded into memory as needed, thus reducing the total amount of memory at runtime or allowing the application to be updated independently. However, it is important to note that the start-up time of the application may become longer and memory fragmentation may occur due to repeated loading/unloading.

Loadable ELF is different from ASMP ELF. The application to be launched runs only on the main core.

In this tutorial, we will use an SD card as the storage location for the ELF files in our application.

16.1. How to build

This is the build procedure via the command line.
When you use IDE, refer to the explanation of the following configuration.

  1. Change directory to sdk

    If you do source build-env.sh script, you can use the tab completion of the config.py tool.

    cd spresense/sdk
    source tools/build-env.sh
    
  2. SDK configuration and building

    Execute the configuration by specifying feature/loadable and device/sdcard as an argument of config.py.

    tools/config.py feature/loadable device/sdcard
    tools/config.py -m
    

    Open the menu config and activate the Hello, World! example. At this time, the application will be built as a loadable ELF if Hello, World! example is set to M.

    [Application Configuration]
      [Examples]
        ["Hello, World!" example] => M

    When the configuration is complete, build it.

    make
    

    When the build is complete, a nuttx.spk file will be created under the sdk folder, and an ELF file hello will be created under sdk/apps/bin. You should copy hello to the SD card separately.

  3. Flashing nuttx.spk into Spresense board

    In this case, the serial port is /dev/ttyUSB0, and the baudrate of the uploading speed is 500000 bps. Please change according to your environment.

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

16.2. Operation check

Open the serial terminal, and run hello application.

  1. Open the serial terminal

    This is an example of using a minicom terminal with /dev/ttyUSB0 as the serial port and 115200 as the baudrate.

    minicom -D /dev/ttyUSB0 -b 115200
    
  2. Execute hello application on NuttShell prompt

    Specify the full path to the hello ELF file on the SD card.

    nsh> /mnt/sd0/hello
    Hello, World!!

16.3. Using the PATH environment variable

NuttShell can set the path of an ELF file using the PATH environment variable as well as the environment variable of bash. The feature/loadable contains settings to use it, but the actual path to use must be set by the user. Open the menu config and set the PATH environment variable to the path of the SD card. With this setting, applications located directly underneath the SD card will be able to run without specifying the full path.

[Binary Loader]
  [Initial PATH Value] => "/mnt/sd0"

Build it and flush nuttx.spk. You can launch hello applications on SD card by typing hello from NuttShell.

nsh> hello
Hello, World!!