Arduinoとmruby/cでESP32を使う方法
STM8のマイコンボード
もっとも安価なマイコンボードは何かと考えると、いろいろな候補が考えられるが、最も有力な候補がSTM8S103F3P6ボードであろう。 書込機が必要だし、他のマイコンボードで代替可能なので、これまで敬遠していたが、Arduinoでの使い方を確かめてみた。 STM社のマイコンボードとしては、USBを扱いたくてblue pillを使ったことがあるが、その時は他の人が作ったプログラムを書き込んで使うに滞っていた。
今回試すボードは、HW-177という刻印があるものである。 Arduinoで使うには、環境設定のところにあるURLに
https://github.com/tenbaht/sduino/raw/master/package_sduino_stm8_index.json
を加える。 ボードマネージャーからstm8などを検索すると、Sduinoが出て来るので、それをインストールする。 最新versionは2019年に出た0.5.0のようだ。 ツールのボードからSTM8S BoardsのSTM8S103F3 Breakout Boardを選択する。 しかし、そのままではコンパイル時にエラーが出た。 ボードマネージャーからArduino megaAVR Boardsもインストールしたら、コンパイルができるようになった。
書き込みをするためには、いくつかの前準備が必要となる。 USBの権限の問題をクリアするために、まずは/etc/udev/rules.d/99-stlink.rulesというファイルを作る。
# ST-Link/V2
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="0666"
# ST-Link/V2-1
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="0666"
# ST-Link/V3
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374e", MODE="0666"
そして、この設定を有効にするために、以下のコマンドを実行する。
sudo udevadm control --reload-rules sudo udevadm trigger
新しいデバイスを使うときには、フラッシュの保護が有効になっていて書き込みができないので、stm8flashのインストールされたフォルダの~/.arduino15/packages/sduino/tools/STM8Tools/2019.02.05/linuxに行って、以下のコマンドでそれを解除する。
./stm8flash -cstlinkv2 -pstm8s103?3 -u
ボードによっては書込端子のGNDが繋っていないものもあるようだが、今回は大丈夫なようだった。 これで準備が完了である。 Arduinoで、書込装置を選択する。 今回はST-Link/V2を用いたが、それとマイコンボードを四本の線で接続する。 そして書き込みボタンを押せば、無事に書き込みが出来て、Lチカが無事に動くことが確認できた。
USBは電源供給のみで書込機が必要だし、ArduinoではC言語しか使えないなど、欠点も多いが、安くしたくて単純な用途には使える気がする。 今後の選択肢の一つとして、使って行こうと思う。
micro ATXからmini PCへ
メインPCを久々に更新することにした。 調べてみると、前回更新したのが2020年の5月だったので、実に6年ぶりということになる。 これまでに使ってきたメインのPCは、筐体はmicro ATXで統一しており、OSはメインのHDDまたはSSDに入れて、dataの入ったHDDを載せかえると、dataの移行の必要が無く、楽にPCの更新ができた。
昨年度、測定用にmini PCを買って使ってみたら、それなりに使い易かったし、性能もあまり問題が無いようだったので、今回はメインPCもmini PCにしてみた。 これまでの様に複数のstorageを載せられないので、homeのみ別のパーティションにして、dataはrsyncを使って移行した。 移行したい設定などのファイルもrsyncで移した。
OSはDebian13だが、sleepのときにhung upする可能性があったので、/etc/systemd/sleep.confでは以下のように指定した。
[Sleep] AllowSuspend=no AllowHibernation=no AllowSuspendThenHibernate=no AllowHybridSleep=no
light-lockerはあまり好きではないので、xscreensaverも入れておいた。 サウンドも認識していないようだったので、pavucontrolを入れて再起動したら、認識したようだ。 スピーカーが無いので、音はならないけど。
少し使った感じでは、うまく動いているようだけど、細々とした違いが出て来るかも知れない。 少しずつ環境を整えていこうと思う。 Linuxの場合、PCの速度による動作の違いは感じ難いけど、動作がきびきびしているように感じる。 さすがに6年の差は大きいのかな。
githubのアカウントを作ってみた
様々なプログラムが掲載されている場所として,githubの存在は以前から知っていたが,これまで詳しいことは知らなかった. Linuxを構成するコードを複数の人で開発するために作られたのがgithubのようだ. 普段からLinuxは使っているが,その事実は知らなかった.
githubのアカウントの作り方を調べたら,なんだかいろいろと面倒な作業が必要のようで,これまで敬遠していた. 知り合いに聞いたところ,googleアカウントでログインすると,そういった面倒なことは無いと言われた. その方法で試してみたら,ほとんど苦労することなく,githubを使うことが出来た.
githubの使い方を検索した時に,なんでこの事が見つからなかったのかと思ったら,これが可能になったのは,半年前のことのようだ. そのため,まだ情報が広がっておらず,昔の情報の方が検索にかかるのだろう.
これで,様々なプログラムをgithub上で開発することが出来るようになった. しかし私は,プログラムの開発はローカルな環境でやるのが好きで,通常は短いプログラムしか作らないので,公開の場所としては,blogなどで十分な気がする. 今後,どの程度使うかは不明だが,気が向いたら使ってみよう.
シンボリックリンクの圧縮
私が構築しているArduinoのUIAPduinoボード用のパッケージは,自分で作ったコードと外部のコードを組合わせて作っているが,外部のコードはできるだけ分離しておきたいと考えている. しかし,ボードをインストールする際のjsonの構造を調べたところ,圧縮されたファイルは,hardwareまたはtoolsの中の別々のフォルダとして展開される. 分離したファイルを,hardwareの中の特定のフォルダの中に展開するということは出来ないので,少し工夫が必要になる. 思いついた方法は,私の作っているArduino環境に組み込んでいるArduinoCore-APIをtoolsの中のフォルダに置いて,そのファイルをhardwareの下にあるメインのコードから呼び出せるようにして,コードを分離するという手法である.
相対pathでapiへのシンボリックリンクを作れば,toolsに入れたArduinoCore-APIも参照することができるのでは無いかと思って試してみたら,ローカルのArduino環境ではうまく動くことを確認した. ボードマネージャからインストールした時にもこれを再現するためには,hardwareの下のファイルを圧縮して展開したときに,シンボリックリンクが有効でなければならない. zipでシンボリックリンクのまま圧縮するには,-ryオプション付きで実行すると良いという情報を見てやってみたが,展開したらリンクの名前の普通のファイルになってしまった. 一方,以下のようにtar.gzで圧縮したら,展開してもシンボリックリンクとして働いた.
tar -zcvf filename.tar.gz folder/
このようにして,少なくともLinuxにおいては,コードを分離した状態で,自分の作ったボード環境をインストールできるようになった.
Windowsでも,tar.gz圧縮したシンボリックリンクが働くという情報もあったので,試してみた. Arduinoを通常の権限で立ち上げて,インストールしようとしたら,シンボリックリンクを作る権限が無いというエラーが出た. そこで,Arduinoを管理者として実行して,インストールしたところ,シンボリックリンクも有効になって,無事に動かすことに成功した. Windowsで確かめるべきことはこれで終わりだと思うので,そろそろWindowsを消しても良いかな. もう少し待った方が良いかな.
自分で作ったArduino環境を,ボードマネージャからインストールできるようになったと思う. LinuxとWindowsではざっとチェックしたけど,Macは持っていないので,動作チェックができない. どうしようかな.
Arduinoのボードマネージャー用のjsonファイル
新しいハードウェア用のパッケージをArduinoにインストールするには,jsonファイルを入力して,ボードマネージャーから指定する. このjsonファイルがどのような構造になっているのかを調べてみた.
基本的には,パッケージの圧縮ファイルと,アーキテクチャ毎のツールの圧縮ファイルが指定されており,すべてのファイルについて,SHA256とファイルサイズを指定する必要がある. SHA256の値が分かっている場合にはそれをコピペすれば良いが,無い場合には,ファイルを用意して以下のコマンドを使って表示された値を使う.
sha256sum filename
また,サイズが分からない場合には,ファイル全体をダウンロードしなくても,以下のようなコマンドでファイルのヘッダを調べることにより,サイズが分かることがある.
wget --spider --server-response url
ちなみに,ローカルにあるファイルを指定する場合には,以下のように記述する必要がある.
file:///home/user/filename.txt
すべてのアーキテクチャで同じファイルを指定する方法が無いか調べたが,分からなかった. jsonファイルを編集すると,ミスをすることもあるが, ブラウザーで開くと,値などが正しく指定されているかや,文法などをチェックできる.
構造が大体分かったので,私がUIAPduino用に作っているch32funを使ったArduino環境を簡単にインストールできるように,jsonファイルを作ってみた. その環境で取り込んで使っているのは,riscv-none-elf-gccとminichlinkとArduinoCoreAPIとch32funとrv003usbである. gccはxpack-dev-toolsの最新のリリースがv15.2.0-1だったので,それを使えば良いだろう. minichlinkはUIAPduinoの本家のファイルを指定すれば良い. ArduinoCoreAPIの最新のリリースがv1.5.2なのでこれを使いたいのだが,hardwareフォルダの中に自動で組み込む方法が分からなかったので,toolにダウンロードさせて,自分でコピーすることにした. ch32funもv1.0rc2というリリースがあったのだが,その中身はch32funでは無く,ch32v003funになっていた. 実質的には同じかも知れないが,環境を構築するときにch32funを使っており,バグなどが起こると嫌なので,ch32funのファイルを自分で組み込むしかないかな. rv003usbについてはリリースがなく,そのフォルダのzipを指定することもできるが,少しでも修正があったらSHA256が一致しなくなるので,これもファイルを自分で組み込むしかないだろう.
私の作った環境とch32funとrv003usbをzipで圧縮したファイルとjsonファイルはローカルに置いて,それ以外はネットから取って来て,簡単にインストールすることができるようになった. aipのフォルダだけはコピーしないといけないが. シンボリックリングなどをつかって,うまくできないかを考えてみよう. gccのバージョンの違いによる問題が起こる可能性もあるが,後でチェックしよう. 新しく立ち上げたPCには,この方法を使って,インストールしようと思う.
UIAPduinoのWindows11での動作確認
Debianを入れる前のWindowsPCが手元にあったので,改良中のUIAPduino用Arduino環境をWindows11でも試してみた. 結果から先に書くと,多少の苦労をしてWindowsでも無事に動くようになり,やはりUSB serialは動かなかったがHIDのターミナルで代替できる.
Windowsを立ち上げたのだが,秘密の質問を登録しろと言われたところで,挫けそうになったが,なんとか耐えて起動させることが出来た. まず,Arduino2.3.8をインストールして,それにAlexanderManderaさんの環境とUIAPduino環境を導入した. 私の環境を使えるようにするために,Linuxで~/Arduino/hardwareにおいていたファイルを,そのままWindowsのUsers/username/Documents/Arduino/hardwareに移した. 原理的にはこれで動くはずなのだが,コンパイル時にいろいろとエラーが出て,うまくいかなかった. Arduino1.8.19も試してみたが,駄目だった.
バグというのは,見つかってから考え直すと当たり前に思えるのだが,それまではなかなか原因が分からないものである. Linuxではコンパイルできるのに,Windowsではコンパイルできない原因を,様々な観点から探ってみたが,うまく行かなかった. AlexanderManderaさんの環境ではWindowsでもコンパイルできるので,そのplatform.txtを元に,少しずつエラーを解消していったら,その過程で原因を見つけることに成功した. megaavrでは,USB CDC用の関数がCDC.hで宣言されている. 私はそれを参考にして,CH32V003用のファイルを作り,同じくCDC.hという名前にした. 一方,rv003usbでは,USB CDCを使うために,TinyUSBのcdc.hというファイルを利用している. Linuxでは大文字と小文字を完全に別の文字として扱っているが,Windowsではこれらを同一視することがあり,これらの2つのファイル名が干渉していたのが問題だった. CDC.hなどをSerialCDC.hなどという名前に変えて,若干の調整を行ったら,Windowsでもコンパイルが通るようになった.
また,以前ざっと確認して駄目だったUSB serialをもう一度確認してみたが,やはりWindowsでは動かなかった. USB serialをプログラムしたUIAPduinoをUSBに繋ぐと,COM3はできるのだが,デバイスマネージャではビックリマークがついているし,Arduinoのシリアルモニタも動かなかった. なんとか工夫して動かせる可能性もあるかも知れないが,簡単では無いだろう. 一方で,HIDを利用したminichlinkのターミナルは,予想していた通りにWindowsでもうまく動いた. ch32funのminichlinkのフォルダから,minichlink.exeとlibusb-1.0.dllをダウンロードして,実行すると「VCRUNTIME140.dllが見つからないため」とか文句を言われるので,Visal C++再頒布可能パッケージのX64版をインストールする. すると,以下のコマンドでminichlinkのターミナルが開く.
minichlink.exe -kT -c 0x1209d003
キーボードからの入力は,画面には出ないがマイコンに出力され,送受信が可能である. minichlinkのターミナルを併用することで,一台のUIAPduinoのみでPCと通信できるので,バグ取りなどの効率が上がるだろう.
Windowsを使うストレスに耐えられなくなって来たので,動作確認の作業はこれで止めにすることにした. 例えば,コードを編集するために,エディタを選んで[一度だけ]を指定していたのだが,面倒になって[常時]を選択したら,拡張子が表示されなくなって,怒りそうになった. 拡張子を常に表示するように設定する方法を調べるのも面倒なので,そのままやったが,他にも様々なストレスを与えてくるので,私にしては頑張った方だろう. このPCは、そろそろDebianで上書きインストールしようと思う.
Windowsで作動することも確認できたので,あとは細かいバグ取りかな. まだ出来ていないことで思い付くことには,SPIとshiftとpulseの動作確認と,I2Cのslaveの実装があるが,他にも何かあるかも知れない.
コンパイラによる動作の違い
このところ,UIAPduino用のArduino環境の改良を行っているが, これまではdebian 13上でch32funを使う際にインストールしたriscv64-unknown-elf-gccなどを使ってコンパイルして来た. 私が改良している環境と同じくch32funを使っているAlexanderManderaさんの環境はriscv-none-elf-gccなどを使用している. 一方,WCHやUIAPduinoやSuzuduinoではriscv-none-embed-gccなどを利用している. このように,riscvには幾つかのコンパイラーがあるので,その種類による違いを検証してみた.
これらのパッケージの名称に含まれている elfはExecutable and Linkable Formatの略のようで標準的なパッケージなのを示すのに対して, embedは組み込み用に特化したものを指すようだ. そして,unknownよりもnoneの方が公式の提供元であることを示すようだ. riscv-none-embed-gccは,ch32の開発元のWCH社が出しているパッケージである. riscv-none-elf-gccは,xPackというプロジェクトのものであるようだ. そして,私が使っているriscv64-unknown-elf-gccは,debianプロジェクトが作ったものなのだろう.
すべての機能をチェックするのは大変なので, USB serialとI2Cを使うプログラムをコンパイルして書き込んで,その挙動を比較してみた. まずは,これまで使っていたriscv64-unknown-elf-gccであるが,当然うまく動作する. ちなみに,コンパイルしたときのサイズは15088 bytesであった. 次にriscv-none-elf-gccだが, AlexanderManderaさんの環境がインストールされていれば,別の環境からも使うことができるようで,platform.txt中で以下のような指定をすると,コンパイラを使うことができた.
compiler.path={runtime.tools.riscv-none-elf-gcc.path}/bin/
newlibをincludeしていた部分をいじる必要があったが,喜ばしいことにこれを使っても無事に動いた. サイズは14020 bytesと少し小さくなったが,バージョンが古いからかも知れない. 最後にriscv-none-embed-gccだが,UIAPduinoのファイルを利用して,以下のように指定した.
compiler.path={runtime.tools.riscv-none-embed-gcc-8.2.0.path}/bin/
サイズは14028 byteであったが,残念なことにUSB serialを認識しなかった. I2Cの方の結果をUSB serialで見ているので,I2Cも動いているか分からなかった. コンパイルオプションなどをいじると,もしかしたら動くかも知れないが,大変そうなので試していない. このコンパイラはch32funとの相性が良くないのかも知れない.
結果として,配布パッケージであるriscv-none-elf-gccでも,USBが無事に動くことが確認できた. 簡単にチェックできるtoneやpwmも大丈夫だったし,おそらく他の機能もほぼ動いているだろう. 今後の検証では,このコンパイラを使って行っていこうと思う.
ついでに,Arduinoのversionによる違いについても調べてみた. 私はArduino1.8.19を使っているが,現在はArduino2系列が主流になりつつある. 丁度,立ち上げたばかりのDebian13のPCがあったので,flatpakでArduino2.3をインストールしたら,一旦ファイラーが動かなくなってしまった. 普段使っているPCでやらなくて良かった. udevとdialout groupの設定をして,AlexanderManderaさんの環境をインストールして,Arduino2.3でも試してみたが,ほぼ問題無いように見える. 別のPCでAppImageでも試したが,libfuse2をインストールする必要はあったが,こちらでも問題無いようである.
Windowsでも同じようなコンパイラを使えば動くだろうが,私の手元には確認できる環境が無い. Windowsの入ったPCを買っても,すぐにLinuxを上書インストールしてしまうので. Debianを入れる前のWindowsPCもあるので,試してみたいとも思わないでも無いのだが,Windowsを使って3分ぐらい経つと,耐え切れなくなって使うのを止めてしまうんだよな. USB serialは動かないのは確かめたし,今回は止めておこうかな.
UIAPduinoでtoneとinterrupt
構築中のUIAPduino用のArduino環境に対して, 先週末に実装したつもりだったtoneとinterruptには,やはりバグがあった. なんとか,一応は動くようになったので,どのようなバグがあったかを説明したい.
まず,toneについてだが,32bitのCPUなので,timerも32bitだろうと思ってコードを書いたのだが,実はtimerは16bitだったので,その部分の修正が必要だった. prescalerが16bitだったので,実質的にはほぼ32bitとして扱えるので,比較的広い周波数の指定ができる状態を保持したまま,無難なコードに書き直した. その過程で,最初は挙動が思った通りにならなかったが,それはArduinoCoreAPIでtoneという関数が宣言されているためだった. また,durationというパラメータを誤って解釈していたのだが,何ms後に自動で音の発生を止めるという意味だった. 最初はdelayで誤魔化していたが,PWMを作っているタイマーで,波の数をカウントして止めることにした. しかし,タイマー割り込みのやり方の情報があまり見つからなかったので,試行錯誤が必要だった. update時の割り込みを許可して,updateをoverflowまたはunderflowに限定して,割り込み関数を有効にしたら,一つの波が終わったときに割り込みがかかる.
TIM1->DMAINTENR |= TIM_UIE; TIM1->CTLR1 |= TIM_URS; NVIC_EnableIRQ( TIM1_UP_IRQn );
そして,割り込みの中で,以下のようにupdateをクリアしないと何度も割り込みがかかってしまう.
TIM1->INTFR &= ~( TIM_UIF );
1を書き込んだらクリアされると思ったら,0を書き込む必要があった. 別の割り込みでは1の方だったので,そっちを試していたら,なかなかうまく行かなかった.
次に,interruptだが,二つの大きなバグがあった. 一つは,rv003usbの中でピンのinterruptを使っているので,USBを使っているときには使えないということだ. バグを発見するために,USB serialを使って動作を確認していると,何だか変な挙動をすることから,そのことに気が付いた. そこで,USBを使っているときには,interruptのコードは取り込まないようにしたが,これは仕方無いだろう. USBを使っていないときには,きちんと動くようにするためには,もう一つのバグを解決する必要があったが,それはC言語とC++の共存に関係する問題だった. attachInterruptなどの関数は,ArduinoCoreAPIで宣言されているので,それらの関数はC++で書かないといけないが,割り込み処理の関数はch32funで定義されているので,C言語で書かないといけないのである. それを意識して,少しだけ修正したら,無事に動くようになった. 気付いてみると当たり前なのだが,これらの事に気が付くのに時間がかかった. こちらは,プログラムの本質的な部分には問題は無かったのだが,上記のような相性の問題だった.
連休中に組み込んだあと2つの機能のshiftとpulseは,それほど複雑では無いので,バグは無いと思いたいが,あるかも. と思ってArduinoCoreAPIを確認したら,2つとも関係する関数の宣言が行われていたので,それに合わせて少し調整を行った. しかし,これらの機能の動作を確かめるのは面倒なので,あまり使わないだろうし,後回しにして良いだろう.
rv003usbも使えるようになったし,コンパイルしたサイズはかなり小さくなったし,目指していたUIAPduino用のArduino環境の条件は,ほぼ満たすものが出来てきたように思う. そういえば,ic2のslaveがまだだった. これもあまり使わないし,動作確認も面倒なんだよな. SPIもとりあえず書いたけど,動作確認していない. ほぼ出来上がってくると,興味がだんだんと薄れてしまうので,今後どこまで仕上げるかな.
2025/3/25追記 rv003usbのフォルダを眺めていたら,demo_extiという名前で,USBとピンinterruptを共存させる例があった. 仕様上,USBで使っているピンと同じ番号のピンは使えないが,それ以外のピンなら, RV003_ADD_EXTI_MASKでマスクを指定して, RV003_ADD_EXTI_HANDLERで処理を行うことができるようだ. しかし,機械語を使って書かなければならなくて,attachInterruptを実現するのは難しそうなので,今は諦めよう.