いろいろな言語でマイコンを動かしてみる C/C++(PlatformIO), TinyGo, MicroPython...

この記事は[DeNA 24 新卒 Advent Calendar 2023](https://qiita.com/advent-calendar/2023/dena-24-newgrad)の21日目の記事です。
同期のみなさんが書かれた他の記事もぜひぜひチェックしてみてください！
vimとかマイコンとか、個人的に興味がある内容もたくさんで面白いです……!!!

## 昨今のマイコン開発
本記事でのマイコンは産業用途でなく、ホビー用途のマイコン(ESP32、ラズピコなど)を示します。
昨今のマイコン開発ではWiFi機能が当たり前に入りつつ、価格も10ドル以下で技適もばっちりなど、誰でも手が出しやすくなってきたように感じます。

最近ではArduino UnoにR4が登場したことでAVR 8bitを脱し、Arduino UnoシリーズもArm 32bitの仲間入りを果たしたり、WiFi搭載モデルが登場したりしています。

安価なマイコンではESP32シリーズが活躍しており、ESP32C3はRISC-Vを採用、ESP32S3ではXtensaとRISC-Vを使い分けるなど新しいアーキテクチャも登場しています。
ファイルシステムやセキュアブート、マルチタスクなど、PC向けOSなのか！？というレベルでいろいろなことができます。

## 記事本編にあたって
今回はマイコン本体ではなく、マイコンをプログラムする側に着目して、様々なマイコンプログラムが可能な言語を触ってみます！

本記事で使用した書き込みホストはArch Linuxですが、Linux系のOSであれば概ね同様で、ポートの名前は異なりますがMacOSでも概ね同様に環境構築可能です！

また本記事で使用するマイコンとしてM5Stack Core2を選びました。
(今回使用するどの言語でも動作が確認できるためです。)

ここからはC/C++ (PlatformIO), TinyGo, MicroPythonの3つについて「言語の概要→個人の所感→環境構築→動作確認」を行った後、紹介しきれなかった言語の説明を行い、最後に簡単な速度比較と全体の感想を述べる流れになります。

## C/C++ (PlatformIO)

### 概要

Cでの開発は昔からある手法で、ホビー用途ではかつてArduinoIDEで一世を風靡しました。しかしArduioIDEの独特の使い心地やライブラリも含めて専用ツールでの書き込みが必要でした。([最近のバージョンではIDEがモダン](https://fabcross.jp/news/2022/20220116_arduino_ide20rc.html)になりましたが……)

最近ではPlatformIOというオールインワンツールが(個人の所感ですが)人気を博しています。

複数マイコン対応ができ、ライブラリ管理もできて、モダンな開発体験ができます。
対応言語はC/C++限定のツールです。

#### 対応マイコン

今回挙げる中で最強の多さです。スクロールするだけで日が暮れる量です。

https://docs.platformio.org/en/latest/boards/index.html

### 所感
:::note info
絵文字は
😍→ポジティブな個人の感想
😢→ネガティブな個人の感想
としています。
:::
#### 😍

- 豊富なマイコン向けライブラリが存在！
- 複数マイコン(マルチボード)対応が容易
- 数多くのマイコンに対応
- C/C++でありながら、ライブラリの依存をiniファイルに書けるので他環境での再現性が高い
- マイコン個別のコンパイラや書込ツールのインストールが不要で全部自動で解決
- platformioが入ればどこでも動く←圧倒的に良い！！！！

#### 😢

- (個人の力不足だが)C/C++での記述が大変(特に文字列操作など)
- 複数マイコン(マルチボード)に対して概ね同じコードが使えるが、内部の依存が暗黙的に違うことがあり、予期しない問題に遭遇しがち

### 環境構築

#### インストール

VSCodeの拡張機能で入れる方法とCLIツールを用いる方法の2種類あります。
前者はただクリックで入れるだけという恐ろしい簡単さで、後者もpacmanやhomebrewなどで簡単に入れられます。

↓VSCode拡張機能

https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide

↓CLI版

https://docs.platformio.org/en/latest/core/installation/index.html

**あくまで個人的な好みですが**、CLI版のほうがサクサク動いて使い勝手も良いので、CLI版について取り上げます。最新のものは公式ドキュメントからご確認ください。

PlatformIOのインストールに関して、
HomeBrewなら

```sh
brew install platformio
```

Arch Linxuなら

```sh
sudo pacman -S platformio
```

でインストールできます。

platformIOのコマンドは`pio`で始まります。

#### プロジェクト作成

[init](https://docs.platformio.org/en/latest/core/userguide/project/cmd_init.html)でプロジェクトを作ることができます。
ボードを指定できる`-b`で`M5 Stack Core2`を指定します。(複数指定も可能で、マルチターゲットなプロジェクトにもできます。)

```sh
mkdir with_pio && cd with_pio
pio project init -b "m5stack-core2"
```

ディレクトリが作られます。src/にプログラムを書いていきます。

```sh
tree .
.
├── include
│   └── README
├── lib
│   └── README
├── platformio.ini
├── src
└── test
    └── README

5 directories, 4 files
```

マイコンやライブラリの指定はplatformio.iniに記述されます。
コマンド経由でも手動でも加筆修正可能です。

```ini:platformio.ini
[env:m5stack-core2]
platform = espressif32
board = m5stack-core2
framework = arduino
```

初期の指定は`framework = arduino`になっていますが、M5系のマイコンだと`framework = espidf`も指定できたりします。


pioでは[`pkg`コマンド](https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html)でボードの追加もマイコンの追加もできて、Gitにおける`checkout`コマンドみたいな汎用性があります。

ライブラリ探しはWebの[公式registory](https://registry.platformio.org/)での検索が便利です。(コマンドからの検索もできますが、こちらのほうがexamplesなどタブで見れてより便利です)



#### プログラム作成

今回は`framework=arduino`なので、Arduinoのようなお作法でプログラムを書けば動きます。
ただしArduino IDEのように標準で色々ライブラリが入っているわけではないので、必要なライブラリは先述の`pkg`コマンドでインストールする必要があります。

今回、M5Stack Core2のディスプレイに文字を描画したく、そのために必要な[m5stack/M5Core2](https://registry.platformio.org/libraries/m5stack/M5Core2)ライブラリをインストールします。

```sh
pio pkg install --library "m5stack/M5Core2@^0.1.8"
```

これで入ります(iniファイルに直書きでも可能です)。

src/にmain.cppを作成して下記プログラムを書き込みます。
M5Stack Core2のディスプレイにアドベントカレンダーのタイトル、作動中のCPUのクロック周波数、アドベントカレンダーリンクのQRコードを描画するプログラムです。
またシリアル通信検証のためにシリアル通信で`Running`を出し続けるようにもしています。

```cpp:src/main.cpp
#include <Arduino.h>
#include <M5Core2.h>

void setup()
{
    M5.begin();
    M5.Lcd.fillScreen(GREEN);
    M5.Lcd.setCursor(10, 10);
    M5.Lcd.setTextColor(RED);
    M5.Lcd.setTextSize(3);
    M5.Lcd.println("DeNA\n New graduate\n  Advent Calendar\n   2023\n");
    M5.Lcd.setTextColor(BLACK);
    M5.Lcd.setTextSize(2);
    M5.Lcd.print("CPU Frequency:");
    M5.Lcd.print(getCpuFrequencyMhz());
    M5.Lcd.println("MHz");
    M5.Lcd.qrcode("https://qiita.com/advent-calendar/2023/dena-24-newgrad", 228, 160, 90, 9);
}

void loop()
{
    Serial.println("Running");
    delay(3000);
}
```

- `M5.Lcd`でM5StackマイコンのLCD機能を呼び出しています。
- `getCpuFrequencyMhz()`はM5StackでなくESP32が提供する関数で、CPUのクロック周波数を取得できます(`setCpuFrequencyMhz()`を使えばクロック周波数を変えることもできます)
- `delay`はArduinoの関数で指定したミリ秒待つものです

といった具合にいろいろなレイヤのものが混ざり合っていて怖さがありますね……

他にもESP32のArduinoはFreeRTOSが入っているので、`xTaskCreatePinnedToCore()`でFrreRTOSのタスクを実行することも可能で、それが`#include`の追加なしに呼ぶことができて、牧歌的な空間になっています。

#### 書き込み

書き込みは`run`コマンドを使用します。コンパイラや書き込みツールは自動で適切なものが選ばれます。
platformio.iniで指定すれば他のツールを明示的に選ぶこともできます。

```sh
pio run -t upload -e m5stack-core2
```

- `-e`の指定で書き込み環境を指定します。
- `-t upload`の指定でビルドだけでなくマイコンへの書き込みまで一度に可能です。
- ポートの指定は`--upload-port /dev/ttyUSB0`のように手動でも記述できますが、自動で識別してくれるので省略します。

これだけで書き込みまでできるはまさに革命です。
ありがちな自分で書き込みツールをmakeでビルドして……とかそういう手間が一切ありません。

### 動作検証
描画できてます！

(M5Stack Core2はバッテリー内臓なので外しても使えます。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621241/4bdc6279-6662-963c-661d-9fcaea76b488.png)




シリアル通信は`device monitor`コマンドで確認できます。
M5Stack Core2をUSBで繋いで見てみます。

```
pio device monitor -b 115200
```

- `-b`で速度を指定できます。
- runと同じくポート選択できますが、記述を省略して自動選択にしています。

```s
--- Terminal on /dev/ttyUSB0 | 115200 8-N-1
--- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
Running
Running
Running
Running
Running
Running
Running
Running
```

`src/main.c`の`loop`関数内で記述したRunningが垂れ流されていることがわかります。

PlatformIoはパッケージマネージャ、ビルド、書き込み、シリアルモニタがすべて揃って書き込みできるオールインワンツールです！！！

## TinyGo

### 概要

[TinyGo](https://github.com/tinygo-org/tinygo)はGoのマイコン向けサブセットです。
マイコンにも依りますが、goroutineを使った開発すらマイコンでできちゃいます。

豆知識：Go 1.21でwebassemblyコンパイルがGo本体にもサポートされましたが、それまではTinyGoがGo→webassemblyへのコンパイル手段としても選ばれていました。

モダン言語のマイコン向け言語の多くがVMタイプなのに対して、TinyGoではLLVMを用いてビルドされ、マイコンに書き込まれます。
内部的にはLLVMを使っているため、新規マイコンの対応が早いのも特徴です。LLVM IRの勝利ですね。

#### 対応マイコン
対応マイコンは先述の通り比較的多いです。

https://tinygo.org/docs/reference/microcontrollers/

M5Stamp C3など最近出たマイコンにも対応しています。
またTinyGoでは新しいマイコンの追加を比較的容易にできるようです。(LLVMがサポートしていれば！！)

https://github.com/tinygo-org/tinygo/wiki/Adding-a-new-board


一方で、対応機能は一部のみです。(GPIOは使えるけど、WiFiモジュールは使えないなど)

例えばM5Stack Core2では下記のようにWiFiやBluetoothだけでなくI2CやPWMも未対応です……

| Interface | Hardware Supported | TinyGo Support |
| --------- | ------------------ | -------------- |
| GPIO      | YES                | YES            |
| UART      | YES                | YES            |
| SPI       | YES                | YES            |
| I2C       | YES                | Not yet        |
| ADC       | YES                | Not yet        |
| PWM       | YES                | Not yet        |
| USBDevice | NO                 | NO             |
| WiFi      | YES                | Not Yet        |
| Bluetooth | YES                | Not yet        |


### 所感

#### 😍

- Go系のカンファレンスでもTinyGo制作事例が登場するなど、日本での利用事例も存在
- マイコンの差異が抽象化できる関数や定数になっていて可読性が高い(例えばマイコン搭載のLEDは`machine.LED`でピン番号を取得できる)
- 環境構築が比較的容易
- LLVMでコンパイルできるマイコンターゲットであれば対応が早い
- 公式ドキュメントに対応マイコン、対応機能が分かりやすくまとまっている。

#### 😢

- 対応マイコンは多いが、対応機能が一部のみ
  - WiFiモジュールを搭載するマイコンでWiFi機能が使えず、ESP32などのSoCの強みを活かしにくい(一部サポートされている環境はあるようです)
  - ~~ディスプレイ付きのマイコンボードでもディスプレイが使えない~~
      - ([コメント](https://qiita.com/Usuyuki/items/8deb26e9c11c0f150f11#comment-05e79098ee2bdfc6fe8c)にて@sago35さんよりご指摘いただいたとおり)driversを用いることで最低限の表示は可能です！！
- ライブラリのドキュメントが乏しい

デメリットは少ないですが、少ないデメリットが致命的で、使い方によっては厳しい側面もあります。
一方でディスプレやWiFi機能が不要な自作キーボードのプログラムなどでは利用事例もあり、言語は適材適所だなと感じさせられます。

### 環境構築

最新の方法は公式ドキュメントからご確認ください。

https://tinygo.org/getting-started/install/

#### インストール

TinyGo自体は、HomeBrewなら

```sh
brew tap tinygo-org/tools
brew install tinygo
```

Arch Linxuなら

```sh
sudo pacman -S tinygo
```

でインストールできます。

追加でGoの環境も必要なので、[rtx](https://github.com/jdx/rtx)などを用いて用意しておきましょう。

<!--
↓↓これ不要っぽい

今回M5Stack Core2に書き込むにあたっては、追加でM5Stack Core2のSoCであるESP32の提供元のEspressif Systems社が提供するマイコン書込ツール[ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html)のインストールも必要です。

ESP-IDFはバージョン起因の問題も生じるので、バージョンごとにGit経由でダウンロードしてmakeでビルドするのが後々便利です。インストールに関しては[公式ドキュメント](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#manual-installation)をご覧ください。
-->

#### プロジェクト作成

プロジェクトの作成は普通のGoと同じです。

```sh
mkdir with_tinygo && cd with_tinygo
go mod init with_tinygo
```

```sh
tree .
.
└── go.mod

1 directory, 1 file
```

#### プログラム作成

Lチカをしつつ、シリアル通信でLEDの状態を表示するプログラムを作成してみます。

```go:main.go
package main

import (
    "machine"
    "time"
)

func main() {
    led := machine.GPIO23
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})

    for {
    	println("High")
        led.High()
        time.Sleep(time.Second)

    	println("Low")
        led.Low()
        time.Sleep(time.Second)
    }
}

```

- `machine.GPIO23`のようにしてGPIOにアクセスできます。
- マイコン固有のものに関しては、マイコンごとのページに定義が書いてあります。 例: [M5Stack Core2](https://tinygo.org/docs/reference/microcontrollers/machine/m5stack-core2/)
- `println`などでシリアル通信に出力を流せます

#### 書き込み
<!--
M5Stack Core2の場合は、最初にESP-IDFの設定が必要です。ESP-IDFのリポジトリのexport.shを実行します。

```sh:コマンド例
. /home/usuyuki/esp/esp-idf/export.sh
```
-->

書き込みは`flash`コマンドを用います。

```sh
tinygo flash -target=m5stack-core2 main.go
```

### 動作検証

GPIO28とGNDにLEDを指すと……(本来は抵抗を挟むべきです。良い子は真似してはいけません)

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621241/0c3dea46-5dab-8ba0-a1b5-fefdd947a234.gif)

(Gifでフレームレートを落としていますが実際は)1秒毎に点滅しています。
アドベントカレンダーらしくクリスマス要素も生まれてよかったです。

~~TinyGo自体にはシリアルモニタが付属していないので、自身で用意する必要があります。~~
([コメント](https://qiita.com/Usuyuki/items/8deb26e9c11c0f150f11#comment-05e79098ee2bdfc6fe8c)にて@sago35さんよりご指摘いただいたとおり)TinyGoにはシリアルモニタも付属しています！


```sh
tinygo monitor --target m5stack-core2
```

↓(ポートは自動で認識してくれます)

```sh
Connected to /dev/ttyUSB0. Press Ctrl-C to exit.
Low
High
Low
High
Low
(無限に続く……)
```

GoでGPIOを用いてLチカしつつ、シリアル通信での出力を確認することができました！

## MicroPython

### 概要

[MicroPython](https://github.com/micropython/micropython)はPythonのマイコン向けサブセットです。

言語自体機能も豊富ですが、マイコン特化の機能もデフォルトで多く提供されており、例えばファイルシステムやネットワークアクセスなどが簡単に使用できます。

言語自体の機能についてより詳しく知りたい方は[Interface 2023年3月号のMicroPython教科書](https://interface.cqpub.co.jp/magazine/202303/)が勉強になります。
また[MicroPythonのESP32クイックリファレンス](https://micropython-docs-ja.readthedocs.io/ja/latest/esp32/quickref.html)を見るだけでもマイコン固有の機能の対応の多さに驚かされます。

低レイヤの機能も使えつつ、高度な機能にもアクセスできる強みがあります。

M5Stack社が提供するGUIでマイコンプログラムができる[UIFlow](https://flow.m5stack.com/)は内部でMicroPythonを用いています。

#### 対応マイコン

インタプリタのダウンロードページから対応しているマイコンを確認できます。

https://micropython.org/download/

この中にある`Vendor:M5 Stack`にはATOMシリーズしか掲載がないですが、M5Stack公式ツールの[UIFlow](https://flow.m5stack.com/)を用いれば、M5Stack社の多くのマイコンでMicroPython(をラップしたGUIプログラミングツール)が使えます。

マイコンごとにピン配置なども異なるため、マイコンの機能を適切に使うにはマイコンにあったインタプリタを用意する必要があります。
[M5Stack公式のMicroPython導入ドキュメント](https://docs.m5stack.com/en/quick_start/core2/mpy)にもUIFlow経由でのMicroPythonが紹介されています。

(M5Stamp C3Uなどは逆にUIFlowが対応しておらずMicroPython公式のインタプリタがあるので、MicroPython公式側のインタプリタを用いるのが良さそうです。
マイコンごとに環境構築の手法が異なり少し厄介です。)

いずれにしてもTinyGoやCとのそれと異なり、基本的にランタイムとプログラムを別個で書き込むことになります。

### 所感

#### 😍

- Pythonの多くの機能が舞い込んでも使える
- マイコン固有の機能も一部対応している(M5Stack Core2のディスプレイやWiFiモジュールが使える)
- 対応マイコンが多い
- 低レイヤな機能にもアクセスできる
- マイコンにインタプリタを書き込んでしまえば、コンパイル不要で簡単にプログラムを試せる

#### 😢

- C/C++と比べると速度が芳しくない
- 環境構築手段が複数あり、それぞれ特徴があり選定が難しく、書き込みまでの工数も多い
- インタプリタ、書込ツール、プログラムが別れているので初期構築に手間がかかる
- マイコンに書き込んで実行するまで動作確認できない(インタプリタ型なのでやむを得ない)

### 環境構築

#### インストール

M5Stack Core2の機能を最大限活かすのであればUIFlowですが、本記事ではオリジナルなMicroPythonで比較を行いため、MicroPython公式の`esptool`を用いた手法を取り上げます。

公式のインストール手順に従います。

https://micropython-docs-ja.readthedocs.io/ja/latest/esp32/tutorial/intro.html#esp32-intro

```sh
pip install esptool
```

まずマイコンのデータを消し去ります。

```sh
esptool.py --port /dev/ttyUSB0 erase_flash
```

先述の通りM5Stack向けはないので、[ESP32 / WROOM](https://micropython.org/download/ESP32_GENERIC/) で代用します。(M5 Stack Core2のSoCはESP32-D0WDQ6-V3ですが、一般的にこの世代はESP32と略されているようです)。

https://micropython.org/download/ESP32_GENERIC/

FirmeWareから最新の.binファイルをダウンロードして、書き込みます。

```sh
cd ~/ダウンロード
esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 ESP32_GENERIC-20231005-v1.21.0.bin
```

これでMicroPythonランタイムがマイコンに導入されました。

試しにシリアル通信経由でプログラムを入力して実行してみます。
任意のツールが利用可能ですが、今回も環境構築の手間を省くためにPlatformIOのツールを用います。
(MicroPythonのデフォルトのbaud rateは115200)

```sh
pio device monitor -b 115200
```
最初は何も出ませんが、キーボードのEnterを押すと`>>>`が出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621241/6ef52758-f3ce-cdf9-db78-d2c36106883e.png)

ここにMicroPythonのコードを入力すると実行できます。

試しにこちらを入力してみます。
```py
import machine
machine.freq()
```
↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621241/abac21d3-f717-5dd3-6b2a-dfe5623ba142.png)
現在のM5Stack Core2のクロック周波数が確認できました！

設定を行えばネットワーク経由でプログラムを書き込んだり、先ほどのようにシリアル通信でも書き込みができます。
ですが度々コピーするのは面倒なので、ファイルをFTP的にPCからマイコンに移動できるツールを導入します。

ファイルをPCから転送するツールもいくつかありますが、簡単にインストールできる[rshell](https://github.com/dhylands/rshell)を今回は使用します。

```sh
pip install rshell
```

後ほど書き込み時に使用します。

#### プロジェクト作成

PlatformIOやTinyGoのような初期化コマンドはないので、ディレクトリを作ってPythonファイルを作成します。

```sh
mkdir with_micropython && cd with_micropython
```

#### プログラム作成

折角なのでMicroPythonのWLAN機能を使って天気情報を取得してみます。
(ssid,passwordは環境に応じて変えてください。)

```python:main.py
import network
import urequests
import json

print("DeNA New graduate Advent Calendar 2023")

ssid = "SSID"
password = "PASSWORD"
url = "https://api.open-meteo.com/v1/forecast?latitude=35.6895&longitude=139.6917&hourly=temperature_2m&start_date=2023-12-24&end_date=2023-12-25"


# WiFiに接続
def connect_wifi():
    station = network.WLAN(network.STA_IF)
    station.active(True)
    station.connect(ssid, password)

    while not station.isconnected():
        pass

    print("接続OK")


# APIで温度取得
def get_temperature_change():
    response = urequests.get(url)
    data = response.json()

    # 時間ごとの温度を表示
    for entry in data["hourly"]["time"]:
        index = data["hourly"]["time"].index(entry)
        temperature = data["hourly"]["temperature_2m"][index]
        print("時間: {}, 温度: {}°C".format(entry, temperature))

    response.close()


connect_wifi()
get_temperature_change()

```

APIは[Open-Metro](https://open-meteo.com)を用いて、Tokyoのクリスマスイブからクリスマスの温度を取得してみます。

ArduinoでもArduinoJson経由でJSONを扱えますが、デフォルトでここまでできるのはさすがモダンな言語です。
(良くも悪くも)型を意識する必要すらありません。

#### 書き込み
下記コマンドで、マイコン上にSSHするかのような状態になります。
```sh
rshell -p /dev/ttyUSB0
```

(catやlsなども使えます。)

それでは、先ほど作成したmain.goをマイコン上にコピーします。
MicroPythonのVM上では`/pyboard`がMicroPython実行時のカレントディレクトリ的な扱いになっています。
MicroPythonではmain.pyがエントリーポイントになって実行されるので、名前はそのままで大丈夫です。


```sh
cp main.py /pyboard
```

実行終了後はCtrl+dでrshellから抜けて大丈夫です。

### 動作検証
例によってPlatformIOのシリアルモニタにて、シリアル通信を行います。
```sh
pio device monitor -b 115200
```

でシリアル通信を確立させた後、M5Stack Core2のリロードボタンを押して再起動させることで、先程書き込んだmain.pyを実行させます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621241/5ab77d4e-85eb-6ff1-f1f7-9396f84d4967.png)


画像の通りAPIを叩いた結果がシリアル通信に出力されました。
今年の東京のクリスマスイブとクリスマスの温度変化が確認できています。

寒いですね……
ということで、MicroPython経由でESP32でネットワークアクセスをして情報を取得することができました！

## 他

### mruby

[mruby](https://github.com/mruby/mruby)は我らが日本を代表する、まつもとゆきひろさんが作られたRubyの組み込みシステム向けバージョンです。
またこれをさらに改善し、低性能なマイコンでも動作するようにした[mrubyc](https://github.com/mrubyc/mrubyc)というのもあります。
これも日本が開発の中心となっており、九州工業大学としまねソフト研究開発センターが共同開発で作っておられます。
今回掲載しようと試みましたが、環境構築に大苦戦し、再現性のある記事が書けなかったため断念しました。

mrubycではPlatformIOを用いた手法もあるようですが、mrubyもMicroPython同様、マイコンごとに環境構築の手法が異なり少し厄介です。

### Rust

RISC-Vマイコンの登場とLLVMの活躍もあり、Rustでもマイコン開発ができるようになってきました。部分的ですが……
[Interface 2023年5月号の特集](https://interface.cqpub.co.jp/2305rust2/)にはstdなRustでM5Stamp C3Uを用いた開発手法が取り上げられています。私も読んでみて、かなり詳しく取り上げられていると感じましたので、Rustでマイコン開発に興味がある方はご覧になってみてください。

nightlyが必要だったりと多少手間はかかりますが、stdなRustでマイコン開発ができるのはとても魅力的です。

### JavaScript

JavaScriptのマイコン向けエンジンは[Espruino](https://github.com/espruino/Espruino)や[JerryScript](https://github.com/jerryscript-project/jerryscript)など複数存在します。
今回検証や環境構築の解説は省きますが、あまりにも様々な環境で動くJavaScriptには脱帽せざるを得ません。

### Python
紹介したMicroPythonとは異なるPythonのマイコン向けサブセットもいくつかあり、GitHubでのスター数では[MicroPython](https://github.com/micropython/micropython)が首位であるものの、教育用途のための[CircuitPython](https://github.com/adafruit/circuitpython)や、Python2を8bitマイコンでも使えるようにした[PyMite](https://github.com/dwhall/p14p)などあるようです。

人間が海を見たがるように、プログラム言語もマイコンに回帰するのではないでしょうか。←？

## 速度を比較してみる
最後に定番の素数判定プログラムで測定してみます。
それぞれ2147483647が素数かを判定するのにかかった時間で、同じアルゴリズムを使用しています。
コンパイラオプションなどはデフォルトのままで、環境構築と書き込みはこの記事の手法、マイコンはM5Stack Core2を用いています。

<details><summary>使用したコード</summary>

```Go:TinyGo
package main

import (
	"math"
	"time"
)

func isPrime(num int) bool {
	if num <= 1 {
		return false
	}
	for i := 2; float64(i) <= math.Sqrt(float64(num)); i++ {
		if num%i == 0 {
			return false
		}
	}
	return true
}

func main() {
	start := time.Now()
	result := isPrime(2147483647)
	end := time.Now()
	println("result: ", result)
	println("time(micro): ", end.Sub(start)/1000)
}

```
```c:C/C++
#include <Arduino.h>

bool is_prime(int num)
{
  if (num <= 1)
  {
    return false;
  }
  for (int i = 2; i <= sqrt(num); i++)
  {
    if (num % i == 0)
      return false;
  }
  return true;
}

void setup()
{
  Serial.begin(115200);
  while (!Serial)
  {
    delay(100);
  }
  int start_time = micros();
  bool result = is_prime(2147483647);
  int end_time = micros();
  Serial.print("result: ");
  if (result)
  {
    Serial.println("prime");
  }
  else
  {
    Serial.println("not prime");
  }

  Serial.print("time(micro): ");
  Serial.println(end_time - start_time);
}

void loop()
{
  Serial.println("done");
  delay(100000);
}

```
```python:MicroPython
import math
import utime

def is_prime(num):
    if num <= 1:
        return False
    for i in range(2, int(math.sqrt(num)) + 1):
        if num % i == 0:
            return False
    return True

start = utime.ticks_us()
result = is_prime(2147483647)
end = utime.ticks_us()
print("result: ", result)
print("time(micro): ", utime.ticks_diff(end, start))
```
</details>


結果は下記のとおりです。

| 言語                       | 実行時間の平均 (μs) |
| -------------------------- | ------------- |
| TinyGo v0.30.0             | 5659          |
| C/C++ (PlatformIO v6.1.11) | 200937        |
| MicroPython v1.21.0        | 912474        |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621241/e540fe8b-d06e-bec2-89ea-bdf2ecaf984e.png)

グラフからも分かる通り、MicroPythonがもっとも遅く、TinyGoが最速となりました。
MicroPythonはインタプリタなので遅いのは妥当な結果ですが、TinyGoがC/C++(PlatformIO)より早いというのは意外です。


[コメント](https://qiita.com/Usuyuki/items/8deb26e9c11c0f150f11#comment-5d5d14cb79073f45c674)にて@fujitanozomuさんよりご指摘いただいたように，C/C++が異常に遅い原因についてはコードに問題があります．
一見普通に実装したように見えてアセンブラレベルではcall命令が多用されており，またsqrtfでなくsqrtを用いるなどオーバーヘッドが多くありました．

最終的に
```diff_c:Cでの素数判定関数
bool is_prime(int num)
{
+  int num_sqrt = sqrtf(num);
-  for (int i = 2; i <= sqrt(num); i++)
+  for (int i = 2; i <= num_sqrt; i++)
  {
    if (num % i == 0)
      return false;
  }
  return true;
```
と変更することで，3625μsという**最速の実行**が可能となりました．
パフォーマンス改善は奥が深い……

## 所感

マイコンの開発手法はたくさん増えましたが、かなり混沌としている印象を受けました。

同じ言語でもマイコン向け言語が複数あったり、同一のマイコン向け言語でもマイコンにより書き込み方が違うなど様々です。
一方でPlatformIOを用いることでC/C++でも快適に開発ができるなど、単に言語の書き心地だけでなく環境全体が重要だと感じさせられました。

## おわり

卒業研究が非常にまずい時期ですが、アドベントカレンダーをうまく利用して卒業研究とも関連のある分野を深めることができて一石二鳥になりました！
やはり締切は人類最大の発明ですね……

ちょっと宣伝です。日常的にブログを趣味でやっていて、記事は[blog.usuyuki.net](https://blog.usuyuki.net)に書いています！
今回はQiitaのアドベントカレンダーなので、Qiitaに書いてみました！

最後までご覧いただきありがとうございました🙇🙇🙇