MATLAB/Simulink Homeの自動コード生成（追加ツールボックス必要なし）を使ってCAN通信を実現する

##MATLAB/Simulink Homeとは
　MATLAB/Simulinkは最近ではキャンパスライセンスなど積極的に展開されており、使われている方も多いと思います。しかしながら会社でMATLAB/Simulinkを導入しようとすると、（あまりにも高すぎて）ライセンス数が少なくちょっとした勉強に会社のネットワークライセンスを消費するわけには行かないということも多いと思います。

　それが理由かはわかりませんが、MATLAB/SimulinkにはMATLAB Homeというのがだいぶ前に追加されました。MATLAB本体が15500円他のツールボックスなどが1つあたり4490円となっています。（1年のライセンスですが）

　MATLAB Homeは個人で勉強するには非常に安価で重宝するのですが、とんでもなくがっかりするポイントがあります。

「コード生成のツールボックスがない」
「コード生成のツールボックスがない」
「コード生成のツールボックスがない」
「コード生成のツールボックスがない」
「コード生成のツールボックスがない」
「コード生成のツールボックスがない」
「Vehicle Network Toolboxがない」

　おそらくコード生成機能を利用されてしまうと、趣味を逸脱しかねないというのもあるかもしれません（例えば自動生成のコードをGithubにあげるとか・・・）
　ただVehicle Network Toolboxは関係ないので、早いところ追加して欲しいところです。（Mathworksさんマジで頼みますDJIのモーターとかCAN通信なので・・・）

###Raspberry PiとSimulink
　コード生成のツールボックスがないのは先ほど書いたとおりなので、それだとタイトルが意味不明ですが、実はなんと、Raspberry PiやArduinoであればコード生成が使用できるのです！（Xilinx ZynqやBeagle Boneでもできるそうですが持っていないので・・・）今回はその使い方を中心に記事にしたいと思います。

##CAN-BUS通信 on Raspberry Pi
　それでは、コード生成について書きたいところですが、何かテーマがないとどうも書く気も起きないので、今回はRaspberry PiでCAN通信を可能にして、Simulinkでコード生成して実行してみたいと思います。
###ハードウェア
　まずはハードウェアの準備ですが、Raspberry PiにはCAN-BUS通信の機能はないので、CANのコントローラとトランシーバが搭載されたHATが必要となります。おすすめは以下の3つでしょうか。

 - 2-Channel CAN-BUS(FD) Shield for Raspberry Pi / Seeed Studio (https://www.seeedstudio.com/2-Channel-CAN-BUS-FD-Shield-for-Raspberry-Pi-p-4072.html)
 - PiCAN2 CAN-Bus Board for Raspberry Pi 2/3 / SK Pang (http://skpang.co.uk/catalog/pican2-canbus-board-for-raspberry-pi-2-p-1475.html)
 - RS485 CAN HAT for Raspberry Pi / Waveshare (https://www.waveshare.com/rs485-can-hat.htm)

私はとりあえず家にあったPiCAN2 Duo CAN-Bus Board for Raspberry Pi 2/3を使用したいと思います。

　Raspberry PiはせっかくなのでRaspberry Pi 4を使いたいところですが、SimulinkがRaspberry Pi 3B+しか対応してない、、、ただそこを無理やりRaspberry Pi 4でチャレンジしてみようと思います。
　
###ソフトウェア
　Raspberry Pi 4はUbuntu 19.10で正式にサポートされたそうですが、今回ROSの対応もしたいと思いますので、ここはあえて18.04を使用します。Mathworksのサポート外とか知ったこっちゃないです。ROSのほうが優先度が高いので、問答無用で18.04にします。（これが後で相当苦労することに・・・）
　Ubuntu 18.04のイメージファイルはここで配布して頂いているようなので、使ってみます（保証はしませんが）https://github.com/TheRemote/Ubuntu-Server-raspi4-unofficial
　SDカードの作成などはいろいろ記事が出ているので割愛します。とりあえず動くようにしましょう。

####MATLABの準備
　MATLAB/Simulinkのインストールが終わっていない人はインストールしましょう。ここではせっかくなので最新のR2019bを使用します。インストールが完了したら、MATLAB Support Package for Raspberry Pi Hardware / Simulink Support Package for Raspberry Pi Hardwareをインストールします。MATLABを起動してアドオンのボタンからRaspberry Piで検索すると出てきますので、インストールしてください。

インストールが完了したら、Raspberry Piの設定をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179542/72f5fdde-1fc2-eeb6-9802-244e38983a7e.png)

はい、Raspberry Pi4は現時点（2019年12月）ではないですね。なので、Raspberry Pi 3B+を選びます。と、ここで先に進めたいところではありますが、セットアップはそう簡単には進まないので、先に準備をしておきます。
　上のUbuntu 18.04のイメージにはRaspberry Piのfirmwareに含まれるDynamic Link Libraryがないので、以下の通り取得しておきます。

```console
$ wget https://github.com/raspberrypi/firmware/archive/1.20190925.tar.gz
$ sudo mv ./opt /
$ sudo vi /etc/ld.so.conf

以下を追加
/opt/vc/lib

$ sudo ldconfig /opt/vc/lib/libmmal.so
```

また、config.txtとcmdline.txtが/boot直下に存在しないので、以下の通りシンボリックリンクをはっておきます。

```console
$ sudo ln -s /boot/firmware/config.txt /boot/config.txt
$ sudo ln -s /boot/firmware/cmdline.txt /boot/cmdline.txt
```

さらにさらに、/dev/spidev0.xのパーミッション設定を修正する必要があります。が、そもそもグループにspiがないので追加からやります。

```console
$ sudo addgroup spi
$ sudo gpasswd -a ubuntu spi
$ sudo chgrp spi /dev/spidev0.0
$ sudo chgrp spi /dev/spidev0.1
$ sudo vi /etc/udev/rules.d/50-spi.rules

で以下を追加
SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"

```

では、作業を続けていきましょう。
まず、Raspberry PiとMATLABが正しく通信が成立すると以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179542/984a104e-171e-4a12-2d56-378e80890235.png)

続いて以下のパッケージのインストールに移ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179542/f24ed26e-f61b-7bb7-7748-514bd908ede5.png)

先ほどのRaspberry Piのファームウェアが正しく入っていないと以下の通りエラーとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179542/f6b0f9a3-7abc-1c3e-e4d8-cce23c7f9a1b.png)

インストールは、すべてのパッケージのインストールはできません。以下のパッケージのインストールは失敗しますが、とりあえず継続できるので進めますが、見ての通りsense-hatに関するものは使用できないのでご注意ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179542/d6f73d6c-10eb-e580-4b08-835f25de5fe5.png)

続いてリブートして完了すればセットアップは完了です。

###CAN BUSを使用したSimulinkモデルの作成
　これまで (R2019aまで)SimulinkとRaspberry PiのSupport Packageを用いてCAN-BUS通信を実現するためには、MCP2515のHATを用意してCAN device driver mcp2515 for Arduino and Raspberry Pi (https://jp.mathworks.com/matlabcentral/fileexchange/64782-can-device-driver-mcp2515-for-arduino-and-raspberry-pi) をインストールする必要がありました。しかしR2019bからMCP2515でCAN通信を実現するためのBlockがRaspberry Pi Support Packageに取り込まれました。なので、とても簡単にSimulinkモデルを作成することができます。

　まずモデルコンフィギュレーションを設定します。ハードウェアで実行することを考慮して設定は以下の感じで設定すると良いと思います。
#####ソルバー
 - 終了時間をInfに設定
 - ソルバーの選択　タイプ：固定　ソルバー：離散
 - ソルバーの詳細　固定ステップサイズ：0.01
 - タスクとサンプル時間オプションは作成したモデルによってチェックしてください（今回はチェックしません）
 
#####ハードウェア実行
 - ハードウェアボード：Raspberry Pi
 - ハードウェアボード設定は以下の項目を環境に合わせて設定してください
    - Board Parameters：IPアドレスなどを適切に設定します
    - Build Options：Buildだけにしておいたほうが勝手に実行されるのを防げます
    - SPI：CAN通信のBaudrateよりも高い値にしておいたほうが良いです
    - CAN：CAN Bus Speedは今回は1MBit/sに設定します。Oscillator Frequencyは装着したHATに搭載されているクロックに合わせてください。Interruptピンも同様です。
    - 他は使い方に合わせて設定してください。

　それではCANのBlockを使用してモデルを作成していきますが、よく見てください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179542/c92f38d6-4ca2-9f91-2458-24cdbbf0e690.png)

CAN Receiveのブロックの設定のOutputにCAN Msgを選択するにはVehicle Network Toolboxが必要とあります。しかし、しかし、しかし、「Vehicle Network Toolboxがない」のでこの機能は使えません（Mathworksさん頼むから買えるようにしてください・・・）

さて、ここでとりあえずCAN Transmitだけを使った以下のようなモデルを作成して、Raspberry Piでモデル実行してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179542/93deba68-6dd4-5aec-13e7-65dde5a9af93.png)

注意するところとしては、Constantの型はuint8にする必要があります。
さてそれでは、ビルドしてみましょう。。。
ビルドでは以下のようなことが行われるようです。
 1. Host PCでコード生成
 2. tarballを作成
 3. sshでRaspberry Piにログインして、ファイルを転送
 4. ファイルを解凍
 5. makeの実行
 6. Host PCとRaspberry Piの”ソースコードを削除”・・・・

まあライセンスを購入しているわけではないので、流用されると困るからソースコードを削除するのもわかりますが、手動でもう一度ビルドできるように消さないでほしいものです。
といっても、一回コード生成されたものを消される前にコピーして中身を見てみましたが、そもそも可読性のかけらもない、関数名や変数名がグチャグチャの状態にしたソースコードになっていたので、消さなくてもええやろと思いますが。。。

ということで、気を取り直してビルドしてみると。。。。
あれ？？？？
あれれ？？？
はい、ビルド通りません・・・
以下のリンクの際にエラーが出ています。

```console
gcc -lrt -lpthread -ldl -lpigpio -lrt  -o ../untitled.elf MW_SPI.c.o MW_MCP2515_CAN.c.o untitled.c.o untitled_data.c.o MW_raspi_init.c.o MW_Pyserver_control.c.o linuxinitialize.c.o ert_main.c.o  -lm -lm -lstdc++
untitled.mk:330: recipe for target '../untitled.elf' failed
```

エラーの内容は、

```console
ert_main.c.o: In function `terminateTask':
ert_main.c:(.text+0x58): undefined reference to `sem_post'
ert_main.c.o: In function `baseRateTask':
ert_main.c:(.text+0xc4): undefined reference to `sem_wait'
ert_main.c.o: In function `main':
ert_main.c:(.text.startup+0x3c): undefined reference to `sem_wait'
MW_MCP2515_CAN.c.o: In function `MW_CANInitializeInterrupt':
MW_MCP2515_CAN.c:(.text+0x5cc): undefined reference to `gpioInitialise'
MW_MCP2515_CAN.c:(.text+0x5d8): undefined reference to `gpioSetMode'
MW_MCP2515_CAN.c:(.text+0x5e4): undefined reference to `gpioSetPullUpDown'
MW_MCP2515_CAN.c:(.text+0x5fc): undefined reference to `gpioSetISRFunc'
linuxinitialize.c.o: In function `schedulerTask':
linuxinitialize.c:(.text+0x24c): undefined reference to `sem_post'
linuxinitialize.c:(.text+0x26c): undefined reference to `sem_post'
linuxinitialize.c.o: In function `my_sem_wait':
linuxinitialize.c:(.text+0x36c): undefined reference to `sem_wait'
linuxinitialize.c.o: In function `mw_CreateTask':
linuxinitialize.c:(.text+0x44c): undefined reference to `pthread_attr_setstacksize'
linuxinitialize.c:(.text+0x490): undefined reference to `pthread_create'
linuxinitialize.c:(.text+0x520): undefined reference to `pthread_attr_setaffinity_np'
linuxinitialize.c.o: In function `myAddBlockForThisEvent':
linuxinitialize.c:(.text+0x61c): undefined reference to `pthread_sigmask'
linuxinitialize.c.o: In function `myRTOSInit':
linuxinitialize.c:(.text+0x8b0): undefined reference to `sem_init'
linuxinitialize.c:(.text+0x8c8): undefined reference to `sem_init'
linuxinitialize.c:(.text+0x948): undefined reference to `pthread_attr_getstacksize'
linuxinitialize.c:(.text+0x9b8): undefined reference to `pthread_create'
linuxinitialize.c:(.text+0x9f4): undefined reference to `pthread_create'
linuxinitialize.c:(.text+0xa4c): undefined reference to `pthread_attr_setstacksize'
collect2: error: ld returned 1 exit status
make: *** [../untitled.elf] Error 1
```

お気づきの方も多いと思いますが、原因は２つあります。
 1. スレッド関連を使用する際のオプションが、-pthreadがなくて-lpthreadになっている。
 2. -lpigpioのオプションが各種.oファイルの前に書かれていてリンクエラー（本来は最後）

１番については修正の方法を見つけたので、以下のコマンドをMATLABで実行してください。

```console
load('~/Documents/MATLAB/SupportPackages/R2019b/toolbox/realtime/targets/raspi/toolchain/gnu_gcc_raspberrypi_toolchain_gmake_glnxa64_v1.0.mat');
tc.BuildConfigurations.values{1}.values{9}.setValue('-lrt -pthread -ldl -g');
tc.BuildConfigurations.values{2}.values{9}.setValue('-lrt -pthread -ldl');
tc.BuildConfigurations.values{3}.values{9}.setValue('-lrt -pthread -ldl');
save('~/Documents/MATLAB/SupportPackages/R2019b/toolbox/realtime/targets/raspi/toolchain/gnu_gcc_raspberrypi_toolchain_gmake_glnxa64_v1.0_new.mat');
```

そのあとターミナルで、

```console
cd ~/Documents/MATLAB/SupportPackages/R2019b/toolbox/realtime/targets/raspi/toolchain/
mv gnu_gcc_raspberrypi_toolchain_gmake_glnxa64_v1.0.mat gnu_gcc_raspberrypi_toolchain_gmake_glnxa64_v1.0_org.mat
mv gnu_gcc_raspberrypi_toolchain_gmake_glnxa64_v1.0_new.mat gnu_gcc_raspberrypi_toolchain_gmake_glnxa64_v1.0.mat
```

これは何をしているかというと、ビルド時の設定が書かれたコンフィグファイルを書き換えて、-lpthreadを-pthreadにしてしまっています。

だた、すごく頑張ったのですが、２つ目の対応策を見つけることはできませんでした。
最悪は、オブジェクトのLinkの作業だけRaspberry Piにログインして手作業で修正したコマンドで行えば、ビルドはできるのですが、今回はCANだけできれば良いので別手法を紹介します。ちなみにGPIOを使ったりする場合はどうにもならないので、手作業でLinkコマンドを実行し直すしかありません。（誰か解決策知ってたら教えてください。もしくはMathworksさん、対応おねがいします）

ということで、対応策は初めのほうで紹介したCAN device driver mcp2515 for Arduino and Raspberry Pi (https://jp.mathworks.com/matlabcentral/fileexchange/64782-can-device-driver-mcp2515-for-arduino-and-raspberry-pi) を使用します。

とりあえず、用意されているExampleを参考に以下のような単純に送信するだけのモデルを作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179542/4a310332-839d-a62c-fb0a-7e0fbd9cdee6.png)

ということで、CANの送信の実行ファイルがRaspberry Pi内に作成されました。
実行ファイルの場所がまたわかりにくいところにありまして、
~/MATLAB_ws/R2019b/home/"Host PCでのusername"/モデル名.elf
に存在します。

またこの実行ファイルはSHED_FIFOで実行されるようなので、実行にはsudo権限が必要のようです。


ということで、以上で実行できるようになったわけですが、今後いろいろ作業次第で、この記事もUpdateしていきますのでよろしくお願いします。