SpresenseでOpenPLCを動かしてみた

マイコンボードでPLCのプログラムを動かす**OpenPLC**は色々なマイコンに対応していますが
標準では**SONY Spresense**には対応していません。
[OpenPLCの公式のフォーラム](https://openplc.discussion.community/)に
SpresenseでOpenPLCを使えるようにできないか　という質問をしたら
[Arduino IDEで開発できるマイコンであれば移植は簡単だという返事を頂きました](https://openplc.discussion.community/post/openplc-on-sony-spresense-12530341?pid=1334244021)  

それで、他のマイコンの設定を参考に **Spresense+Extension board** に移植しました。
(とりあえず動くようになったレベルで細かな検証は行っていません。)

必要なハードウエアー
[Spresense MAIN Board](https://developer.sony.com/ja/develop/spresense/developer-tools/introduction#_spresense%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9C%E3%83%BC%E3%83%89)
[Spresense Extension board](https://developer.sony.com/ja/develop/spresense/developer-tools/introduction#_spresense%E6%8B%A1%E5%BC%B5%E3%83%9C%E3%83%BC%E3%83%89)

必要な開発環境
Arduino IDE + Spresense Arduino board package
([arduino IDEにSpresense環境のセットアップはこちらを参照してください](https://developer.sony.com/develop/spresense/docs/arduino_set_up_ja.html))
[OpenPLC Editer](https://openplcproject.com/)

移植の内容
※フォルダーは自分の環境で書いていますので、ご自身の環境に書き直してください。
1. C:\Users\USER\OpenPLC_Editor\editor\arduino\src\halフォルダへの .hal ファイルの追加
2. C:\Users\USER\OpenPLC_Editor\editor\arduino\src\halフォルダへの .cpp ファイルの追加
3. C:\Users\USER\OpenPLC_Editor\editor\arduino\examples\Baremetal\hals.json ファイルにエントリを追加
4. C:\Users\USER\OpenPLC_Editor\editor\arduino\builder.py の修正  
となります。

### 1. .hal ファイルの追加  
```spresense_extention.hal
# HAL configuration
# Format :
# Board type, FQBN
Spresene extention, SPRESENSE:spresense:spresense
```
### 2. .cpp ファイルの追加 
入出力の設定(定義)は次のようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/668f7f82-7445-335c-40f7-2e2c2347350f.png)
 
```spresense_extention.cpp
#include <stdlib.h>
extern "C" {
#include "openplc.h"
}
#include "Arduino.h"

//OpenPLC HAL for Spresense Extension

/******************PINOUT CONFIGURATION*******************
Digital In: 0, 1, 2, 4, 7           (%IX0.0 - %IX0.4)
Digital Out: 8, 10, 11, 12, 13      (%QX0.0 - %QX0.4)
Analog In: A0, A1, A2, A3, A4, A5   (%IW0 - %IW5)
Analog Out: 3, 5, 6, 9              (%QW0 - %QW3)
**********************************************************/

//Define the number of inputs and outputs for this board (mapping for the Arduino Uno)
#define NUM_DISCRETE_INPUT          5
#define NUM_ANALOG_INPUT            6
#define NUM_DISCRETE_OUTPUT         5
#define NUM_ANALOG_OUTPUT           4

//Create the I/O pin masks
uint8_t pinMask_DIN[] = {0, 1, 2, 4, 7};
uint8_t pinMask_AIN[] = {A0, A1, A2, A3, A4, A5};
uint8_t pinMask_DOUT[] = {8, 10, 11, 12, 13};
uint8_t pinMask_AOUT[] = {3, 5, 6, 9};

void hardwareInit()
{
    for (int i = 0; i < NUM_DISCRETE_INPUT; i++)
    {
		uint8_t pin = pinMask_DIN[i];
        pinMode(pin, INPUT);
    }
    
    for (int i = 0; i < NUM_ANALOG_INPUT; i++)
    {
		uint8_t pin = pinMask_AIN[i];
        pinMode(pin, INPUT);
    }
    
    for (int i = 0; i < NUM_DISCRETE_OUTPUT; i++)
    {
		uint8_t pin = pinMask_DOUT[i];
        pinMode(pin, OUTPUT);
    }

    for (int i = 0; i < NUM_ANALOG_OUTPUT; i++)
    {
		uint8_t pin = pinMask_AOUT[i];
        pinMode(pin, OUTPUT);
    }
}

void updateInputBuffers()
{
    for (int i = 0; i < NUM_DISCRETE_INPUT; i++)
    {
		uint8_t pin = pinMask_DIN[i];
        if (bool_input[i/8][i%8] != NULL) 
            *bool_input[i/8][i%8] = digitalRead(pin);
    }
    
    for (int i = 0; i < NUM_ANALOG_INPUT; i++)
    {
		uint8_t pin = pinMask_AIN[i];
        if (int_input[i] != NULL)
            *int_input[i] = (analogRead(pin) * 64);
    }
}

void updateOutputBuffers()
{
    for (int i = 0; i < NUM_DISCRETE_OUTPUT; i++)
    {
		uint8_t pin = pinMask_DOUT[i];
        if (bool_output[i/8][i%8] != NULL) 
            digitalWrite(pin, *bool_output[i/8][i%8]);
    }
    for (int i = 0; i < NUM_ANALOG_OUTPUT; i++)
    {
		uint8_t pin = pinMask_AOUT[i];
        if (int_output[i] != NULL) 
            analogWrite(pin, (*int_output[i] / 256));
    }
}
```
### 3. hals.json ファイルにエントリを追加
```
,
    "Spresense Extention": {
        "platform": "SPRESENSE:spresense:spresense",
        "define": "BOARD_SPRESENSE",
        "source": "spresense_extention.cpp"
    } 
```
一番上の **,** は追加するエントリーの前の
**}** の 後ろに付けてください。

### 4. builder.py の修正
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/57357aa4-a545-a621-6949-969e318f402b.png)
builder.pyファイルに188行～194行を追加します。  
```
        
        // For Spresense
        compiler_logs += runCommand(cli_command +
                                    ' core install spresense:spresense')
        wx.CallAfter(txtCtrl.SetValue, compiler_logs)
        wx.CallAfter(scrollToEnd, txtCtrl)


```


以上の修正・追加を行った後に **Spresense** を接続して、接続された**COMポート**を確認してください。
**OpenPLC Editer** を起動して
ファイル → Tutorials Exampls → 2.Blink
を開きます。
ビットの設定の Blink_led のLocation に **%QX0.4** を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/1ee481b8-7bf0-7b95-24b1-cf60980b36f2.png)
Arduinoアイコンをクリックするとビルドが始り、成功すると、
**Upload to Arduino Board** ダイアログが表示されます。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/07ba6f2f-391f-bd56-f402-ea860e6af69e.png)
ここで **Board Type** に **Spresense Extention** が追加されているはずです。
 **Board Type** で**Spresense Extention**を選択して、**COM Port:を先ほど確認したポート**に合わせます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/f8738902-c9e7-564b-edbc-b5da284e0a81.png)
**Uploadボタン**を押すと**コンパイル**が始まります。
Compilation output: に
Restarting the board ...
reboot

Done!
と表示されると、コンパイルとアップロードが**正常に終了**しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/a522429b-20dc-4006-f59f-a7e15418a604.png)

これで Spresense で OpenPLCで作成した**ラダープログラム**が走ります。

以上速足での説明となりましたが、**SpresenseでOpenPLC**が走ることが確認出来ました。
OpenPLCに関して深堀をしていないので不具合があるかもしれません、
お気付きの点はご指摘下さい。

また**Modbus**に関してはテストしていませんので今後の課題とさせてください。

ソースコードはこちらで公開しています。
https://github.com/kaz19610303/OpenPLC_test_bench/tree/main/Spresense

なおArduino UNE用に作ったテストベンチボードをSpresense用に作り直していますので
コチラは出来上がり次第公開します。

自分的にはSpresenseの開発環境にOpenPLCが加わったのでちょっとうれしいです。
Spresense、OpenPLCどちらからも公式に認定されているわけではありませんが(笑)
いままで Youtube にOpenPLCに関してArduino UNOをベースにした動画をアップしていましたが、
これからはSpresenseベースの動画に切り替えたいと思っています。