【MATLAB】便利！S-Function Builderの使い方-Arduino連携ライブラリの自作-

# はじめに
Simulinkは既存のブロックを組み合わせることでプログラミングすることが可能な、いわゆるビジュアルプログラミングツールであり、通常の使用方法においては
組み込み御用達言語
Ｃ言語やC++を使う必要がありません。やったー！

しかしながら、<b>諸般の事情</b>によりSimulink内でC言語を使わざるを得ないケース*というのが大人にはあります。その際に準備された拡張機能がS-Functionで、これはSimulink上でC言語やC++を扱う事が可能です。
(※主に過去の資産をそのまま流用したい場合やシミュレーションを高速化したい場合)


このように書くと別にS-Function良いじゃん、という雰囲気になりますが、サンプルコードが<b>どう見ても呪文にしか見えなかったり</b>と迷路に迷い込むこと請け合いです。
（[S-Function Examples](https://jp.mathworks.com/help/simulink/sfg/s-function-examples.html) C S-Function Examples を参照）

<b>そんな貴方に救いの手を！</b>　というのが表題のS-Function Builderとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/54cca84d-c42f-b365-44aa-b6c603bd2ca6.png)

これは何かというと、美しいGUIを入力していけばS-Functionのコードを半自動生成してくれる便利ツールとなります。

ただしドキュメントが英語しかなかったり（[Build S-Functions Automatically Using S-Function Builder](https://jp.mathworks.com/help/simulink/sfg/s-function-builder-dialog-box.html)）何を設定すればよいのか分からない欄が多かったりとそれなりにトラップがあります。このため本記事ではS-Function Builderの使い方をサクっと説明します。

なお本記事はMATLAB Expo2021 LTでの発表「[MATLAB Homeでのモータ制御MBD開発に向けた“脱獄指南書”](https://speakerdeck.com/motorcontrolman/matlab-home-prison-break)」の解説をシリーズ化したものの記事の番外編を兼ねます。このため、[３章](https://qiita.com/motorcontrolman/private/756f175c0675f564790f#3-arduino%E9%80%A3%E6%90%BA%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E8%87%AA%E4%BD%9C)以降にてArduino連携も交えた解説を行うものとします。

[①Arduino Dueに３相同期・三角波・相補PWMを打たせる](https://qiita.com/motorcontrolman/items/ef6670f9dff0f63cdc50)
[②Arduino DueでPWM山同期AD変換を行う](https://qiita.com/motorcontrolman/items/ba2cd0fb200501186194)
[番外編 実質無料! Tiの３相GaNインバータの使い方](https://qiita.com/motorcontrolman/items/95560fd38545637fad2a)
[③Arduino Dueの2ch DACを利用する](https://qiita.com/motorcontrolman/items/b1cf04450ad158e1e9f9)
<b>番外編 便利！S-Function Builderの使い方-Arduino連携ライブラリの自作</b>
④MATLAB/SimulinkでArduinoのレジスタを叩く
⑤MATLAB/SimulinkとArduino DueでPWM同期の制御を実現する


# 1. 基本的な使い方
## 1.1 初期設定
S-Function BuilderをSimulink画面上に配置、ダブルクリックすることで編集画面が現れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/4164e821-d309-ebc1-1033-dd2bd7d7396e.png)

詳細設定は[公式ドキュメント](https://jp.mathworks.com/help/simulink/sfg/s-function-builder-dialog-box.html)に任せるとして、サクっと使う場合は下記３つの設定だけ行えばよいです。

①S-Function名：任意の名前を付けましょう。ここではtestと付けます。
②離散時間の数：とりあえず「１」を設定しましょう。
③直達：デフォルトでチェックが入っているのでそのままにします。

その他の設定についてはひとまず不要です。
　
# 2. コーディングの実施
上記設定を行った場合のコーディング箇所としては下記４か所あります。
①Start_wrapper　内
②Outputs_wrapper　内
③Update_wrapper　内（離散時間の数を非ゼロに設定した場合のみ利用可能）
④Terminate_wrapper　内

①は起動時の処理を、④は終了時の処理を書く箇所というのはお分かりの通りかと思います。
②と③はいずれも通常時の処理を書く箇所ですが、その違いがちょっと分かりにくいので下記実例で示します。

## 2.1 Outputs_wrapper内に記載(オススメ)
入力信号に対し出力信号を遅れなく得ることが出来ます。
入力信号を２倍して出力する処理をOutputs_wrapperに記載した場合の結果は下記です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/9ee0c12a-cdb4-cfaf-5420-20e6020a6e27.png)

Simulinkのゲインブロックと同等の結果が得られていることが分かるかと思います。
なお基本設定における「直達」はOutputs_wrapper内における入力信号の利用可否を意味しており、チェックを外した場合はS-Function Builderブロックへの入力信号を利用できなくなります。
このため「直達」のチェックは基本的に入れておく必要があります。

## 2.2 Update_wrapper内に記載(違いの分かる人向き)
Outputs_wrapper とは異なる下記の結果が得られます。
なお、ソルバは固定ステップサイズ0.1sに設定してあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/6e54f8f9-19f9-3131-3c19-24f164c7248d.png)

Update_wrapper内に記載した場合との差異としては下記２点です。
・初期値として0が出力される
・入力信号の変化に対し出力信号が１シミュレーション周期遅れる

ちなみに初期値として0が出力さていますが、特段どこかで設定を行ったわけではなくデフォルトで初期値0が出力されるようです。
(このへんのいいかげんさがMATLAB/Simulinkの良いところというか何というか…)
ちなみに②③の両方にコードを書いた場合は②が優先されるようです。


## 2.3 ビルド
コーディングが完了したら、右上の「ビルド」ボタンを押します。
ビルドログにて、S-Function Builderから生成されたコードが問題なくビルドされたかどうかを確認したのちに、S-Function Builder編集ウィンドゥを閉じましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/718d5cf4-9068-ae0c-30ea-cdbb18d26c2f.png)

ビルドボタンを押さずに閉じた場合、もしくはビルド失敗した場合はコーディングの内容が結果に反映されないので注意下さい。

S-Function Builderを通常使用する場合のフローとしては上記となります。ね、簡単でしょう？


# 3. Arduino連携ライブラリの自作
これ以降ではS-Function Builderを用いたArduino連携ライブラリの作成方法について説明します。

## 3.1 基本的なコーディング方法
### ポイント：#ifndef MATLAB_MEX_FILE　を駆使しろ
タイトルだけ見ても意味不明と思いますので、まずは下記のS-Function Builderサンプルコード（S-Function Builderへの入力信号に応じたLチカ）を確認頂きましょう。`#ifndef MATLAB_MEX_FILE`は３度登場します。

```c
/* Includes_BEGIN */
#include <math.h>
#ifndef MATLAB_MEX_FILE
#include <arduino.h>
#endif
/* Includes_END */

/* Externs_BEGIN */
/* extern double func(double a); */
/* Externs_END */

void test_Start_wrapper(real_T *xD)
{
/* Start_BEGIN */
#ifndef MATLAB_MEX_FILE
    pinMode(13, OUTPUT);
#endif
/* Start_END */
}

void test_Outputs_wrapper(const real_T *inputSignal,
                          real_T *outputSignal,
                          const real_T *xD)
{
/* Output_BEGIN */
#ifndef MATLAB_MEX_FILE
    if(inputSignal[0] > 0)
        digitalWrite(13, HIGH);
    else
        digitalWrite(13, LOW);
#endif
/* Output_END */
}

void test_Update_wrapper(const real_T *inputSignal,
                         real_T *outputSignal,
                         real_T *xD)
{
/* Update_BEGIN */

/* Update_END */
}

void test_Terminate_wrapper(real_T *xD)
{
/* Terminate_BEGIN */

/* Terminate_END */
}
```

魔法の言葉である`#ifndef MATLAB_MEX_FILE` から `#endif`までの間に記載したコードは下記の扱いとなります。

<b>①Simulinkを通常実行する場合：コードに含まないものとして扱われる
②arduino配布用コード生成を行う場合：コードに含むもとのして扱われる</b>

逆説的に、`digitalWrite`のようなarduino固有の関数やヘッダのインクルード `#include <arduino.h>`をS-Function Builderにそのまま記述した場合、ビルドエラーとなります。
もしarduino連携ライブラリを作成しようとしてS-Function Builderがビルドエラーになった場合は`#ifndef MATLAB_MEX_FILE` にて除外処理が正しく出来ているかどうかを確認しましょう。

### [備考] Arduino向けコードが誤っていたらどうなるか
Arduino向けコード、すなわち`#ifndef MATLAB_MEX_FILE` 内のコード内容に問題があった場合にはどうなるかと言うと、この場合 S-Function Builderではビルドエラーとなりません。例えば...

```c
void test_Start_wrapper(void)
{
/* Start_BEGIN */
#ifndef MATLAB_MEX_FILE
    pinMode(13, OUTPUT) あああ
#endif
/* Start_END */
}
```
のように、あからさまなエラー要因があってもS-Function Builderのビルドは通ります。
最終的にどうなるかと言うと、Arduinoに書き込もうと「ビルド、配布および起動」を押した際にエラーとなります。(少し面食らうと思います)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/18e980b4-2b90-b825-9145-5d0140b874e5.png)


キャプチャ画像省略しますが、上記のコードをビルドした場合のエラーメッセージは<b>100行</b>ぐらい出てくるため、何がまずかったかの対処がしにくい問題があります。
エラーメッセージ内を「error」で検索かけるなりして対処しましょう。


## 3.2 上級者向け　自作ライブラリの作成・使用 
3.1に示したような、Arduino向けコードをS-Function Builderに直書きした場合について下記２点の問題があります。

<b>①エラーメッセージが長く、どの箇所が原因なのかの特定がしにくい。
②ビルド時間が長くトライ&エラーがしにくい。</b>

このような場合の対処方法として、Arduino向けコードをS-Function Builder内ではなくライブラリに記載、S-Function Builder内にてインクルードして用いる方法が挙げられます。
具体的には、arduino自作ライブラリを作成するとき同様に`.h`ファイルや`.cpp`ファイルを別途作成しここにコードの記載を行います。

なお、ここで記載するS-Function Builderにおける自作ライブラリの使用方法は過去開発済み資産を流用する場合にも有効ですのでご参考として下さい。

## 3.2.1 自作ライブラリを作成する
ライブラリ自体の作成方法としては[外部記事を参照頂く](https://nobita-rx7.hatenablog.com/entry/28488026)ものとして、ここでは以降で使用するサンプルコードのみを示すものとします。
内容としては、ライブラリ内部でカウンタを加算してき閾値を超えたらONするものとなります。(サンプルにするには少しだけ複雑ですが、こんなことも出来ますよーというアピールのため。)

ヘッダファイル

```LED_ON_CNT.h
extern int16_t Counter;
int16_t CountUP(void);
void LED_SetUP(void);
void LED_ON(void);
```

ソースファイル

```LED_ON_CNT.cpp
#include <Arduino.h>
#include "LED_ON_CNT.h"

int16_t Counter = 1;
int16_t CountUP(void){
	Counter ++;
	if(Counter > 1000)
		Counter = 0;

	return Counter;
}

void LED_SetUP(void){
	pinMode(13, OUTPUT);
}

void LED_ON(void){
	if(Counter > 500)
		digitalWrite(13, HIGH);
    	else
        	digitalWrite(13, LOW);
}
```

## 3.2.2 ライブラリパスを通す
作成した`.h`、`.cpp`を適当なフォルダに格納し、S-Function Builderにてパスを通します。
パスの設定画面はS-Function Builderの入出力信号設定ウィンドウの横タブ「ライブラリ」にありますので、まずはクリックして有効化します。

その上で、「タグ」あたりを右クリックして「Add PATH」からライブラリパスを設定できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/54030523-e38f-ed31-1aa4-417a9d32a6c5.png)

「タグ」はいくつか設定がありますが、「<b>INC_PATH</b>」を使っておけば余程問題ないです。


## 3.2.3 ライブラリを使用する
#### 最大のポイント：.cppもincludeしなければならない
個人的に最も躓いたポイントはここになります。
通常のプログラミングであれば、ライブラリ作成時に`.h`と`.cpp`に分けて作成した場合、ライブラリを使用する場合は`.h`のみインクルードすればＯＫと思います。
しかしながら、S-Function Builder の場合`.cpp`をインクルードしないとビルドエラーとなります。

#### ライブラリ使用例
上記にて作成した`LED_ON_CNT.h`および`LED_ON_CNT.cpp`をライブラリとして使用した場合のS-Function Builderコードを下記に記載します。

```c
/* Includes_BEGIN */
#include <math.h>
#ifndef MATLAB_MEX_FILE
#include <arduino.h>
#include "LED_ON_CNT.h"
#include "LED_ON_CNT.cpp"
#endif
/* Includes_END */

/* Externs_BEGIN */
/* extern double func(double a); */
/* Externs_END */

void test_Start_wrapper(void)
{
/* Start_BEGIN */
#ifndef MATLAB_MEX_FILE
    LED_SetUP();
#endif
/* Start_END */
}

void test_Outputs_wrapper(void)
{
/* Output_BEGIN */
#ifndef MATLAB_MEX_FILE
    CountUP();
    LED_ON();
#endif
/* Output_END */
}

void test_Terminate_wrapper(void)
{
/* Terminate_BEGIN */
/*
 * カスタム終了コードをここに配置します。
 */
/* Terminate_END */
}
```
上記S-Function Builderコードを使用したSimulinkモデルは下記です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/4018f568-38ce-0560-6536-d061b22c4b10.png)

Triggerd Subsystemを使用せず、S-Function Builderブロックをダイレクトに設置、カウントアップのための関数`CountUP` を `test_Outputs_wrapper`内に記載していますが、特に問題なくカウントアップが行われてArduinoをLチカさせることが出来ました。
（なお、固定ステップサイズ`1e-3`を設定した場合において１秒ごとのLチカとなります。アンチョコ集参照。）

# おわりに

ここまで読んで頂いた方はなんとなく分かると思いますが、２章まではお手軽に使えそうな空気が漂っていますが３章以降、だんだんと雲行きが怪しくなってきます。(特に3.2以降)
とはいえ、誰もが躓きそうなところはしっかりとケアしたつもりではいますのでArduinoライブラリを自作してみたい！という方は是非トライしてみて下さい。
そしてSimulink+Arduino芸の沼へようこそ。




# アンチョコ集(随時更新)

<details><summary>本文に書くまでもない細かなノウハウなどはここに記載。</summary><div>

### S-Function Builderのコール周期について
上記で少し匂わせましたが、S-Function BuilderブロックをSimulink画面にダイレクトに設置した場合は固定ステップサイズがコール周期となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/452b1ec7-0e0b-2c49-e373-cd28b7368dba.png)

`LED_ON_CNT.cpp`にてカウンタが500を上回ったらLEDオン、カウンタが1000を上回ったらクリア...というように記載しているため固定ステップサイズ`1e-3`で1秒ごとのL地下になります。

### .cppの関数内部を微修正した場合、S-Function Builderはどうすべき？
例えば、LEDオンする閾値を`500`から`100`に変えたような場合にS-Function Builderを開きなおしてビルドする必要があるか？　ということですがこれに対する解はNoとなります。

S-Function Builder内のコードと.cppのコードとの接続部が変更となった場合(例えば、関数の型を変更した場合)などはS-Function Builderを開き修正を施す必要があります、当たり前ですが。

個人的には、わざわざS-Function Builder側のビルド必要か？　を考えるのが面倒なので`.cpp`ないし`.h`を修正するたびにS-Function Builderを開いてビルドしなおしています。

### エクスターナルモードの使用について
うまくいかない。

### extern宣言した変数の扱い
S-Function Builder内で呼ぶことはできるが、外部に引き回せないかも。

ビルダ内で+4した場合→OK

simulink外部を経由する場合




</div></details>