#3 テンプレートファイルとシェルスクリプトの作成【SPIKE-RTでロボコンに出よう!!】

# 目次

https://qiita.com/koushiro/items/482162807339f4d18516

| 章 | タイトル | 内容 |
|:-:|:-:|:-:|
| 1 | [はじめに](#1-はじめに) | 今回やることについて |
| 2 | [テンプレートの作成](#2-テンプレートの作成) | workspaceフォルダ・テンプレートファイルを作成する |
| 3 | [シェルスクリプトの作成](#3-シェルスクリプトの作成) | シェルスクリプトを作成する |
| 4 | [シェルスクリプトを実行する](#4-シェルスクリプトを実行する) | 作成したシェルスクリプトを実行してみる |
| 5 | [まとめ](#5-まとめ) | 今回のまとめ |

# 1. はじめに

前々回、前回と環境構築を行ってきました。
特に前回はプログラムファイルを書くための「エディタ」としてVisual Studio Codeを導入しましたね。
その環境構築も今回で最後となります。

今回は環境構築の仕上げとして、「テンプレートファイル」と「シェルスクリプト」を作成したいと思います。

## テンプレートファイル

大抵のプログラミングソフトは新規のプロジェクトを作成すると、必要な部分は書かれているが、動作にかかわる部分は空のファイルが生成されると思います。
SPIKE-RTでの開発も同じようにしたいわけですが、その「必要な部分は書かれているが、動作にかかわる部分は空のファイル」を用意しなければなりません。
これをテンプレートして用意しておき、新しいプロジェクトを作成するときはそのテンプレートからコピーしてこようという考えです。

## シェルスクリプト

今までUbuntuのターミナルで何度かコマンドを打ってきたかと思いますが、良く使うコマンドは毎度毎度打ち直すのも面倒なものです。
特に、前々回紹介したdockerを起動する以下のコマンド、長いし複数行に渡るしで間違いを誘発可能性すらあります。

```terminal:Ubuntu
$ sudo docker run --rm -it \
  -v $(pwd):$(pwd) -w $(pwd) \
  -u "$(id -u $USER):$(id -g $USER)" -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro \
  ghcr.io/spike-rt/spike-rt:rich /bin/bash

```

こういったコマンドを「スクリプトファイル」として登録しておき、コマンドを実行するにはファイルを指定するだけでOKにしようというのがシェルスクリプトです。

以上の二つを今回は作成していきます。

# 2. テンプレートの作成

まずは、プロジェクトのテンプレートとなるファイルを作成していきます。

事前準備として、VSCodeを起動し、「リモートエクスプローラー」から「spike-rt」フォルダを選択、「エクスプローラー」画面でspike-rtフォルダ内のファイルが見える状態にしておいてください。

![start_vscode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/8bec9f23-1600-7d59-6763-d77ac903439b.png)

![start_vscode2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/8ebfb323-9934-e609-fda1-6eee78d45715.png)

## workspace フォルダの作成

テンプレートファイルを作成する前に、「プロジェクト」を管理するためのフォルダ **「workspace」** を作成しておきましょう。
尚、これ以降、一つのプログラムの単位を  **「プロジェクト」**  と呼ぶことにします。
LEGO標準 SPIKEソフトでは、以下の画面のプロジェクトにあたると考えてください。

![spikesoft_project.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/846cce9c-cac2-fdaf-b7cc-060943b14e8a.png)

話を戻して、「workspace」フォルダを「spike-rt」フォルダ直下に作成します。
VSCodeでやるならば、下の画像のようにどのフォルダも選択していない状態で、赤丸の新規フォルダ作成ボタンを押せば、「spike-rt」フォルダ直下に新しいフォルダが作成されます。
これの名前を「workspace」としてあげてください。

![workspace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/c0c2ea43-1c0f-0d61-ef13-00a55351e8ff.png)

この「workspace」フォルダ内に、「プロジェクト」は管理していくこととします。

尚、workspaceフォルダ内のファイル構成は以下のようになります。

```
workspace
└ プロジェクト名
    ├ プロジェクト名.c
    ├ プロジェクト名.h
    ├ プロジェクト名.cdl
    ├ プロジェクト名.cfg
    └ Makefile.inc
```

## template フォルダの作成

ではいよいよ、templateフォルダを作成していきます。
扱いとしては、「template」というプロジェクトを作成するといった形です。

先ほど作成した「workspace」フォルダの下に「template」フォルダを作成してください。
VSCodeで行う場合は、下の画像のように「workspace」フォルダを選択した状態で、赤丸の新規フォルダ作成ボタンを押すことで「workspace」フォルダの下にフォルダが作成されます。
これの名前を「template」としてあげてください。

![template_folder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/71a4a437-7e92-6849-c86a-b62daf6230bb.png)

次はこのフォルダ内にテンプレートファイルを作成していきます。

## template ファイルの作成

先ほどと同じ手順で、「template」フォルダを選択した状態で、一つ左隣の新規ファイル作成ボタンを押すことで、「template」フォルダの下に新しいファイルを作成することが出来るので、以下の5ファイルを作成・書き込みしてください。

### template.c

プログラムのメインとなるファイルです。
基本的には、このファイル内の`Main`という関数内に、プログラムを書いていきます。

```c:template.c
#include <stdlib.h>
#include <stdio.h>
#include <kernel.h>

#include <spike/hub/system.h>

#include <template.h>

#include "spike/pup/motor.h"
#include "spike/pup/colorsensor.h"
#include "spike/pup/forcesensor.h"
#include "spike/pup/ultrasonicsensor.h"

#include "spike/hub/battery.h"
#include "spike/hub/button.h"
#include "spike/hub/display.h"
#include "spike/hub/imu.h"
#include "spike/hub/light.h"
#include "spike/hub/speaker.h"

#include <pbio/color.h>

#include "kernel_cfg.h"
#include "syssvc/serial.h"

void Main(intptr_t exinf)
{
  // ここからプログラムを書く
  exit(0);
}
```

### template.h

Cファイルの補佐的な役割となるヘッダーファイルです。
関数のプロトタイプ宣言(詳しくは関数の章で解説)や定数の定義などが行われています。

```c:template.h
#ifdef __cplusplus
extern "C" {
#endif

#include <kernel.h>

/*
 *  タスクの優先度の定義
 */
#define MAIN_PRIORITY	5		/* メインタスクの優先度 */

#define HIGH_PRIORITY	9		/* 並行実行されるタスクの優先度 */
#define MID_PRIORITY	10
#define LOW_PRIORITY	11

/*
 * Definitions of Port ID for Serial Adapter
 */
#define SIO_USART_F_PORTID    1
#define SIO_USB_PORTID        2
#define SIO_BLUETOOTH_PORTID  3
#define SIO_TEST_PORTID       4

#ifndef STACK_SIZE
#define	STACK_SIZE		4096		/* タスクのスタックサイズ */
#endif /* STACK_SIZE */

/*
 *  関数のプロトタイプ宣言
 */
#ifndef TOPPERS_MACRO_ONLY

extern void Main(intptr_t exinf);

#endif /* TOPPERS_MACRO_ONLY */
```

### template.cfg

タスク管理をするファイルです。
SPIKE-RTではマルチタスク(異なる2つのプログラムを同時に走らせること)を実現することが出来ますが、そのタスクの作成、および設定をこのファイル内で行います。

```c:template.cfg
INCLUDE("tecsgen.cfg");

#include "template.h"
CRE_TSK(MAIN_TASK, { TA_ACT, 0, Main, MAIN_PRIORITY, STACK_SIZE, NULL });
```

### template.cdl

カーネル(ASP3)の部分で、あれこれ定義をしてくれているファイルです。(基本的には触りません。)

```template.cdl
/*
 *  カーネルオブジェクトの定義
 */
import(<kernel.cdl>);

/*
 *  ターゲット非依存のセルタイプの定義
 */
import("syssvc/tSerialPort.cdl");
import("syssvc/tSerialAdapter.cdl");
import("syssvc/tSysLog.cdl");
import("syssvc/tSysLogAdapter.cdl");
import("syssvc/tLogTask.cdl");
import("syssvc/tBanner.cdl");

/*
 *  ターゲット依存部の取り込み
 */
import("target.cdl");

import("serial/tSIOAsyncPortTest.cdl");

/*
 *  「セルの組上げ記述」とは，"cell"で始まる行から，それに対応する"};"
 *  の行までのことを言う．
 */

/*
 *    システムログ機能のアダプタの組上げ記述
 *
 *  システムログ機能のアダプタは，C言語で記述されたコードから，TECSベー
 *  スのシステムログ機能を呼び出すためのセルである．システムログ機能の
 *  サービスコール（syslog，syslog_0〜syslog_5，t_perrorを含む）を呼び
 *  出さない場合には，以下のセルの組上げ記述を削除してよい．
 */
cell tSysLogAdapter SysLogAdapter {
  cSysLog = SysLog.eSysLog;
};

/*
 *    シリアルインタフェースドライバのアダプタの組上げ記述
 *
 *  シリアルインタフェースドライバのアダプタは，C言語で記述されたコー
 *  ドから，TECSベースのシリアルインタフェースドライバを呼び出すための
 *  セルである．シリアルインタフェースドライバのサービスコールを呼び出
 *  さない場合には，以下のセルの組上げ記述を削除してよい．
 */
cell tSerialAdapter SerialAdapter {
  cSerialPort[0] = SerialPort1.eSerialPort;
  cSerialPort[1] = SerialPortUSB1.eSerialPort;
  cSerialPort[2] = SerialPortBluetooth1.eSerialPort;
  cSerialPort[3] = SerialPortTest1.eSerialPort;
};

/*
 *    システムログ機能の組上げ記述
 *
 *  システムログ機能を外す場合には，以下のセルの組上げ記述を削除し，コ
 *  ンパイルオプションに-DTOPPERS_OMIT_SYSLOGを追加すればよい．ただし，
 *  システムログタスクはシステムログ機能を使用するため，それも外すこと
 *  が必要である．また，システムログ機能のアダプタも外さなければならな
 *  い．tecsgenが警告メッセージを出すが，無視してよい．
 */
cell tSysLog SysLog {
  logBufferSize = 32;          /* ログバッファのサイズ */
  initLogMask = C_EXP("LOG_UPTO(LOG_NOTICE)");
                    /* ログバッファに記録すべき重要度 */
  initLowMask = C_EXP("LOG_UPTO(LOG_WARNING)");
                       /* 低レベル出力すべき重要度 */
  /* 低レベル出力との結合 */
  cPutLog = PutLogTarget.ePutLog;
};

/*
 *    シリアルインタフェースドライバの組上げ記述
 *
 *  シリアルインタフェースドライバを外す場合には，以下のセルの組上げ記
 *  述を削除すればよい．ただし，システムログタスクはシリアルインタフェー
 *  スドライバを使用するため，それも外すことが必要である．また，シリア
 *  ルインタフェースドライバのアダプタも外さなければならない．
 */
cell tSerialPort SerialPort1 {
  receiveBufferSize = 256;      /* 受信バッファのサイズ */
  sendBufferSize    = 256;      /* 送信バッファのサイズ */

  /* ターゲット依存部との結合 */
  cSIOPort = SIOPortTarget1.eSIOPort;
  eiSIOCBR <= SIOPortTarget1.ciSIOCBR;  /* コールバック */
};

cell tSerialAsyncPort SerialPortUSB1 {
  receiveBufferSize = 256;      /* 受信バッファのサイズ */
  sendBufferSize    = 256;      /* 送信バッファのサイズ */

  /* ターゲット依存部との結合 */
  cSIOPort = SIOPortPybricksUSB1.eSIOPort;
  eSIOCBR <= SIOPortPybricksUSB1.cSIOCBR;  /* コールバック */
};

cell tSerialAsyncPort SerialPortBluetooth1 {
  receiveBufferSize = 256;      /* 受信バッファのサイズ */
  sendBufferSize    = 256;      /* 送信バッファのサイズ */

  /* ターゲット依存部との結合 */
  cSIOPort = SIOPortPybricksBluetooth1.eSIOPort;
  eSIOCBR <= SIOPortPybricksBluetooth1.cSIOCBR;  /* コールバック */
};

cell tSIOAsyncPortTest SIOPortTest1 {
};

cell tSerialAsyncPort SerialPortTest1 {
  receiveBufferSize = 10;     /* 受信バッファのサイズ */
  sendBufferSize    = 10;     /* 送信バッファのサイズ */

  /* ターゲット依存部との結合 */
  cSIOPort = SIOPortTest1.eSIOPort;
  eSIOCBR <= SIOPortTest1.cSIOCBR;  /* コールバック */
};

/*
 *    システムログタスクの組上げ記述
 *
 *  システムログタスクを外す場合には，以下のセルの組上げ記述を削除すれ
 *  ばよい．
 */
cell tLogTask LogTask {
  priority  = 3;          /* システムログタスクの優先度 */
  stackSize = LogTaskStackSize;  /* システムログタスクのスタックサイズ */

  /* シリアルインタフェースドライバとの結合 */
  cSerialPort        = SerialPort1.eSerialPort;
  cnSerialPortManage = SerialPort1.enSerialPortManage;

  /* システムログ機能との結合 */
  cSysLog = SysLog.eSysLog;

  /* 低レベル出力との結合 */
  cPutLog = PutLogTarget.ePutLog;
};

/*
 *    カーネル起動メッセージ出力の組上げ記述
 *
 *  カーネル起動メッセージの出力を外す場合には，以下のセルの組上げ記述
 *  を削除すればよい．
 */
cell tBanner Banner {
  /* 属性の設定 */
  targetName      = BannerTargetName;
  copyrightNotice = BannerCopyrightNotice;
};

```

### Makefile.inc

コンパイルの時に用いられるmakeのルールについて書き記しているファイルです。
主にソースコードファイルを追加するときなどに用いられます。

```Makefile.inc
NEWFILE_COBJS = 
APPL_COBJS += $(addprefix , $(NEWFILE_COBJS))
```

これら全てを追加したら、VSCode上には以下のように表示されているはずです。

![temp_files.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/783da4d1-c385-2e63-f400-7345a0e57b47.png)

これにて、テンプレートフォルダ・ファイルの作成が完了です。
この作成したテンプレートファイルを元に、次に作成するシェルスクリプトが自動的に新規プロジェクトを生成することになります。

# 3. シェルスクリプトの作成

次にシェルスクリプトを作成していきます。
実は「spike-rt」フォルダ内には既に「scripts」というフォルダが存在し、この中にもいくつかシェルスクリプトが存在しています。
これから作成するシェルスクリプトにはこれらをより使いやすいように編集したものも含まれます。

## 作成するシェルスクリプト

はじめに、どのようなシェルスクリプトを作成するかを示しておきます。

| ファイル名 | 用途 | 使い方 |
|:-:|:-:|:-:|
| start.sh | Dockerの起動 | `start.sh` |
| new_project.sh | 新規プロジェクトの作成 | `new_project.sh test1` |
| create.py | ファイルコピーを行う | new_project.shが実行するPythonのコード |
| build.sh | プロジェクトのビルド | `build.sh test1` |
| write.sh | 実行ファイルのハブへの書き込み | `write.sh test1` |

これら4つのシェルスクリプト及び1つのPythonスクリプトを作成していきます。

## シェルスクリプト用フォルダの作成

はじめに、作成するシェルスクリプトをまとめておくフォルダを作っておきたいと思います。

前章で「workspace」フォルダを作成したように、「spike-rt」フォルダ直下に「new_scripts」というフォルダを作成してください。

![new_scripts_folder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/493329d7-673d-e0db-d988-5070270d986c.png)

ここに、上述の5つのファイルをまとめていきます。
ではここから、スクリプトを作成していきます。

## start.sh

作成した「new_scripts」の中に新しいファイルを作成し、以下のスクリプトを書き込んでください。

```sh:start.sh
sudo docker run --rm -it \
  -v $(pwd):$(pwd) -w $(pwd) \
  -u "$(id -u $USER):$(id -g $USER)" -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro \
  ghcr.io/spike-rt/spike-rt:rich /bin/bash
```

### 使い方

このシェルスクリプトはDockerを起動するためのスクリプトです。
プロジェクトをビルドするためにはDocker上で行わなければならないので、ビルドするまえにこのスクリプトにより起動をしておきましょう。

以下に使い方を示します。

```terminal:Ubuntu
~/spike-rt$ ./new_scripts/start.sh
```

## new_project.sh

同様に新規ファイル作成し、以下のスクリプトを書き込んでください。

```sh:new_project.sh
#!/bin/bash
if [ "$1" = "" ]; then
	echo "no argument"
	exit 1
fi

appname=$1

if [ -e "workspace/${appname}" ]; then
	echo "${appname} already exists."
	exit 1
else
	mkdir "workspace/${appname}"
	cd workspace
	../tools/python/bin/python3 ../new_scripts/create.py $appname
	chmod -R 777 ../workspace/$appname/
fi
```

### 使い方

このシェルスクリプトは、新しいプロジェクトを作成するためのスクリプトです。
前述のとおり、「template」フォルダのテキストからコピーして新たなプロジェクトフォルダを生成します。
使用する際には、ファイル名の後ろに「新しいプロジェクトの名前」を付加します。

以下に使い方を示します。

```terminal:Ubuntu
~/spike-rt$ ./new_scripts/new_project.sh test1
```

## create.py 

同様に新規ファイル作成し、以下のスクリプトを書き込んでください。

```python:create.py
import os,sys

appname = sys.argv[1]

extenders = ['h', 'c', 'cdl','cfg']

for extender in extenders:
    f = open('template/template.'+extender, 'r', encoding='utf-8')
    s = f.read().replace('template.h', appname+'.h')
    f.close()

    f = open(appname + '/' + appname + '.' + extender, 'w',encoding='utf-8')
    s = f.write(s)
    f.close()

f = open('template/Makefile.inc', 'r', encoding='utf-8')
s = f.read()
f.close()

f = open(appname + '/Makefile.inc', 'w', encoding='utf-8')
s = f.write(s)
f.close()
```

### 内容

このファイルは自分で実行するものではなく、先ほどの`new_project.sh`が実行するファイルなので、使い方は特にありません。
内容については「template」フォルダ内のファイルを順次読み出し、新規プロジェクトのファイルに書き込んでいきます。
ただし、プロジェクト内のファイル名は随時変わるので、ヘッダーファイルの読み出し部分のみ、そのプロジェクトの名前になるようにリネームをかけています。

## build.sh

同様に新規ファイル作成し、以下のスクリプトを書き込んでください。

```sh:build.sh
#!/bin/sh
BUILD_DIR=${BUILD_DIR:-build}
JOB_NUM=${JOB_NUM:-$(nproc)}

if [ "$1" = "" ]; then
	    echo "no argument"
	    exit 1
fi
appname=$1
if [ ! -e "workspace/${appname}" ]; then
	  echo "Such a '${appname}' dosen't exit."
	  exit 1
fi

# cd spike-rt
mkdir -p $BUILD_DIR
cd $BUILD_DIR

if [ ! -e "obj-primehub_kernel" ]; then
	  mkdir -p obj-primehub_kernel
	  (cd obj-primehub_kernel && ../../asp3/configure.rb -T primehub_gcc -f -m ../../common/kernel.mk)
fi

if [ ! -e "obj-primehub_${appname}" ]; then
	  mkdir -p obj-primehub_$appname
	  (cd obj-primehub_$appname && ../../asp3/configure.rb -T primehub_gcc -L ../obj-primehub_kernel -a ../../workspace/$appname/ -A $appname -m ../../common/app.mk)
fi

# Currently, build libpybricks.a, first of all.
# libkernel.a itself does not depend on libpybricks.a, 
# but some codes in libkernel.a depend on header files which is cloned by `make libpybricks.a`. 
(cd obj-primehub_kernel && make libpybricks.a -j $JOB_NUM && make libkernel.a -j $JOB_NUM)

(cd obj-primehub_$appname && make -j $JOB_NUM && make asp.bin -j $JOB_NUM)

(cd obj-primehub_$appname && cp asp.bin ../../workspace/$appname/asp.bin)

cd ..
```

### 使い方

このシェルスクリプトは、プロジェクトをビルドするためのスクリプトです。
これを実行するときは、必ずDockerを起動させた上で実行してください。
第1回でも説明しまあしたが、Dockerを起動すると以下のようにターミナルの色が変わります。

![terminals.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/172421dd-cb9b-c028-1fdf-a832f434b176.png)

上がDocker起動状態、下が起動していないただのUbuntuの状態です。
ビルドはこの上の状態で行ってください。

ビルドが完了すると、プロジェクトフォルダ内に`asp.bin`という実行ファイルが生成されます。

又、使用する際には、ファイル名の後ろに「新しいプロジェクトの名前」を付加します。
以下に使い方を示します。

```terminal:Ubuntu (Docker起動)
~/spike-rt$ ./new_scripts/build.sh test1
```

## write.sh

同様に新規ファイル作成し、以下のスクリプトを書き込んでください。

```sh:write.sh
if [ "$1" = "" ]; then
	    echo "no argument"
	    exit 1
fi
appname=$1
if [ ! -e "workspace/${appname}" ]; then
	  echo "Such a '${appname}' dosen't exit."
	  exit 1
fi

sudo scripts/deploy-dfu.sh workspace/$appname/asp.bin
```

### 使い方

このシェルスクリプトは、ビルドした結果生成された実行ファイル`asp.bin`をハブに書き込むためのスクリプトです。
このファイルではプロジェクトの指定だけ行っており、実際の書き込み処理は「scripts」フォルダ内の「deploy-dfu.sh」が行っています。

使用する際には、ファイル名の後ろに「新しいプロジェクトの名前」を付加します。
以下に使い方を示します。

```terminal:Ubuntu
~spike-rt$ ./new_scripts/write.sh test1
```

すべてのファイルを作り終えたら、以下のようになっているはずです。

![new_scripts_5files.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/0d371bde-478e-73f3-9740-dc0dce1bd642.png)

## 実行権限を付与する

実はこれで終わりではありません。
大抵、Linuxではシェルスクリプトによる不用意な操作を行わせないために、アクセス制限がかけられています。
シェルスクリプトにはじめからすべての権限を渡していると、悪意をもったプログラムによりPCが攻撃される危険性があるからです。

今作成したシェルスクリプトにもその制限がかかっているので、実行するための権限を付与します。

以下のコマンドを一行ずつ実行します。

```terminal:Ubuntu
$ cd new_scripts
$ sudo chmod 777 start.sh
$ sudo chmod 777 new_project.sh
$ sudo chmod 777 build.sh
$ sudo chmod 777 write.sh
$ cd ../
```

一行目のコマンドで「new_scripts」フォルダに移動し、2~5行目で権限を付与しています。
`chmod`コマンドによりアクセス権限を細かく指定できるのですが、777というのはとても簡単に言えば、誰でも「読み」「書き」「実行」が出来る状態を示します。

詳しくはこちら👇をご参照ください。

https://qiita.com/dev63/items/ceeaebeb7f587020c14c

以上で、シェルスクリプトの準備は完了です。

# 4. シェルスクリプトを実行する

では、実際にシェルスクリプトを動かして、SPIKE-RTでの開発の流れをつかんでおきましょう。

## VSCode内でのターミナルの起動について

まず、いつも通りUbuntuのターミナルを開かないといけないのですが、実はVSCode内で開くことが出来ます。
VSCodeのウィンドウの上部、「ターミナル」から「新しいターミナル」を選択すると、ウィンドウ下部にターミナルが表示されます。

![vscode_new_terminal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/6c9d258d-49cd-268a-93a2-28b3746b8a40.png)

![vscode_terminal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/4c0225c8-ebde-6dd3-f817-b7c11214939d.png)

さらに、上の画像で緑色の四角でターミナル部分を示しましたが、その右上にある「ターミナルの分割」ボタンを押すと、以下の画像のようにターミナルを2つ横並びに置くことが出来ます。

![terminal_split.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/1ff24f8b-9f03-0009-aed2-1959a2fb7729.png)

![vscode_terminal_split.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/8f383ab7-7577-3e99-41ff-29e7a19bb2c9.png)

私のおすすめの運用としては、このターミナルの分割機能を利用して、「左側でビルド」「右側でその他の操作(主にハブへの書き込み)」をすると良いと思います。

上述のように、`build.sh`を実行するにはDockerを起動している状態でなければなりません。
そこで、ターミナル➀で`start.sh`でDockerを起動、そのままターミナル➀で`build.sh`を実行すれば良いという形です。

一方で、`new_project.sh`や`write.sh`は普通のUbuntuのターミナルで実行します。
そこで、ターミナル➁ではDockerは起動せず、そのまま上記2ファイルを実行することとします。

## ターミナル➀でDockerを起動

まずは、ターミナル➀の方でDockerを起動してみましょう。

```terminal:Ubuntu
$ ./new_scripts/start.sh
```

![terminal_script1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/b6cf1d77-306a-82d4-2f66-860a9dfa5ae4.png)

これで、左側はDockerが起動した状態になりました。

## ターミナル➁で新しいプロジェクトを作成

次に、ターミナル➁の方で、新しいプロジェクトを作成してみましょう。
今回は、「test1」というプロジェクトを作ってみます。

```terminal:Ubuntu
$ ./new_scripts/new_project.sh test1
```

![terminal_script2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/cbcb4009-cb44-fd2f-9190-36e90824c0f8.png)

これで、左側のエクスプローラー部分にて、「workspace」フォルダ下に「test1」というフォルダ、さらにその下にプロジェクトを構成する5ファイルが作成されます。

## Cファイルを編集する

では、生成された`test1.c`を編集して、簡単なプログラムを作ってみましょう。
コードの内容についての詳しい説明はここでは行いません。

`test1.c`を開いて、以下のコードを書き込んでください。

```c:test1.c
#include <stdlib.h>
#include <kernel.h>

#include <spike/hub/system.h>

#include <test1.h>

#include "spike/pup/motor.h"
#include "spike/pup/colorsensor.h"
#include "spike/pup/forcesensor.h"
#include "spike/pup/ultrasonicsensor.h"

#include "spike/hub/battery.h"
#include "spike/hub/button.h"
#include "spike/hub/display.h"
#include "spike/hub/imu.h"
#include "spike/hub/light.h"
#include "spike/hub/speaker.h"

#include <pbio/color.h>

#include "kernel_cfg.h"
#include "syssvc/serial.h"

void Main(intptr_t exinf)
{
  // ここからプログラムを書く

  while(1){
    hub_light_on_color(PBIO_COLOR_RED);
    dly_tsk(1*1000*1000);
    hub_light_on_color(PBIO_COLOR_GREEN);
    dly_tsk(1*1000*1000);
    hub_light_on_color(PBIO_COLOR_BLUE);
    dly_tsk(1*1000*1000);
  }

  exit(0);
}

```

ざっくり説明すると、ハブボタンのLEDを赤⇒緑⇒青と順番に点灯するプログラムです。
このプログラムをビルドして、ハブブロックに書き込んでみたいと思います。

## ターミナル➀でビルド

先ほどターミナル➀でDockerを起動しました。
このターミナル➀で、プロジェクト「test1」をビルドしたいと思います。

```terminal:Ubuntu (Docker)
$ ./new_scripts/build.sh test1
```

正しくビルドが出来ると、ターミナル➀の最下部に以下のように表示されるはずです。

```terminal:Ubuntu (Docker)
arm-none-eabi-objcopy -O binary -S asp asp.bin
```

また、エクスプローラーの「test1」フォルダ内に`asp.bin`という実行ファイルが生成されているはずです。

![terminal_script3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/71d0bb61-8422-dc6a-abbb-4b79b0ca95a0.png)

これで、プロジェクトのビルドが完了です。

## ハブブロックの接続

生成された実行ファイルをハブブロックに書き込むために、まずはPC及びWSLとハブブロックを接続します。
接続方法は第1回で説明した通り、ハブブロックをDFUモードで接続するというものです。

ハブブロックの上面にある、Bluetoothアイコンが印字された丸いボタンを押し、押し続けた状態で、USBケーブルによりHUBブロックをPCと接続します。
接続した直後、Bluetoothボタンの淵が紫色に点灯します。

この状態のままさらに5秒程押し続けると、消灯⇒赤⇒緑⇒青の順に光り始めます。
これで、ハブブロックを「DFUモード」でPCと接続することができます。

![spike_dfu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/5bde467d-a085-bee2-deb0-c5745e30406f.gif)

次に、PowerShellを管理者権限で起動して、usbipdを使ってハブブロックをWSLにアタッチします。
これも第1回で説明していますが、以下のコマンドにより接続が可能です。

```powershell:Powershell
USB デバイスの確認
> usbipd wsl list
BUSID  VID:PID    DEVICE                                                        STATE
2-1    0694:0008  LEGO Technic Large Hub in DFU Mode                            Not attached
2-2    0781:5571  USB 大容量記憶装置                                            Not attached
2-5    27c6:538d  Goodix fingerprint                                            Not attached
2-6    0c45:671b  Integrated Webcam                                             Not attached
2-10   0cf3:e007  Qualcomm QCA61x4A Bluetooth                                   Not attached


Version 3.2.0まで
> usbipd wsl attach --busid 2-1 --auto-attach
Attached

Version 4.0.0以降
> usbipd attach --wsl --busid 2-1 --auto-attach
Attached
```

上記のように、Attachedと表示されれば、準備完了です。
尚、このハブブロックとの接続方法については、第1回の補足記事で詳しく説明していますので、必要に応じてそちらを参照してください。

https://qiita.com/koushiro/items/c0ace8e363c7fab45f0d#hub%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AE%E6%8E%A5%E7%B6%9A

## ターミナル②でハブブロックに書き込み

ハブブロックとの接続も出来たところで、いよいよ実行ファイルをハブブロックに書き込みたいと思います。

ターミナル②で、以下のコマンドを実行しましょう。

```terminal:Ubuntu
$ ./new_scripts/write.sh test1
```

すると、以下のような書き込みインジケータが表示されるはずです。

![terminal_script4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/7ee0a54e-e108-b07e-a51c-899388559f3a.png)

インジケータが100%に達し、Finishedと出れば書き込み完了です。
書き込みが完了すると、ハブブロックのDFUモードは解除され、通常モードに戻ります。
と同時に、書き込んだ実行ファイルがすぐに実行され、ハブボタンのLEDが赤⇒緑⇒青と光るはずです。

これで、一通り開発の流れを確認する事が出来ました。

# 5. まとめ

今回はテンプレートファイルとシェルスクリプトを作成し、プログラム開発をより効率的に行えるよう環境を整えました。
以上で開発環境構築は終了です。ここまで大変お疲れ様でした。

いよいよ、次回からはロボットプログラミングに関わる部分へ入っていきたいと思います。
最初はモータの使い方を解説する予定です。

前回: [#2 エディタを使おう(VisualStudio Code)](https://qiita.com/koushiro/items/dbfc45be2e05a44bc273)
次回: [#4 モータの使い方](https://qiita.com/koushiro/items/0471376a9feb9b53b178)