CNCに耳かきカメラ

 

 サンコーの耳かき用の小型カメラをCNCに搭載しました。bCNCの設定メモ代わりに記事を残します。購入したUSBカメラは残念ながら品切れ状態で、故障した時の替えが手に入りません。。

 CNCは基板切削をメインに使用しており、ワークが軸に対して水平に固定できているかを確認するためにカメラを使用します。そもそも基板は外形カットするので生基板の位置など関係ない様に思えそうですが意外とそんなことはありません。状況にもよりますが1枚の生基板からは異なるサイズの基板を数枚切り出すことが多く、最後に1mm程度の余裕でぴったり割り付けられることがあります。このようなとき最初の切り出しがずれていると悲しいことになってしまうのです。

 bCNCがカメラを認識すると以下のような表示になります。画面を見ながら水平、垂直移動をしてワークの位置調整をしたり原点出しを行うことができます。

 カメラは3Dプリンタでステーを作りスピンドルの根元に固定しました。

 bCNCはAnaconda powershellから使用しています。Anacondaを使う理由は、すっぴんからインストールしようとしたときにカメラに必要なPythonライブラリの依存関係の解消にハマったためです。Anacondaの場合はOpenCVをpipでインストールするだけでカメラを使用することができるようになりました。以下、自分のたどった手順のメモです。

https://repo.anaconda.com/archive/

よりAnaconda2-2019.07-Windows-x86_64.exeをインストール

(bCNCのインストールは割愛)

pip install opencv-python がエラーとなる

OpenCVをインストールする
https://github.com/opencv/opencv/releases
opencv-4.5.0-vc14_vc15.exe
をDL

pip install opencv-python を実行

エラーは出るがファイルはインストールされているっぽい

環境変数を設定
OPENCV_DIR=C:\usr\local\opencv\build
PYTHONPATH=C:\usr\local\opencv\build\python

PATHに以下を追加
C:\usr\local\opencv\build\bin
C:\usr\local\opencv\build\x64\vc15\bin


Anaconda powershell を再起動し
bCNC.bat を起動する

カメラ動作

 

Codec2WalkieTalkieが気になる

 こんなgitリポジトリを見つけてしまいました。

 readmeを読むと、Codec2で圧縮した音声をデータ通信用の無線モジュールに流してデジタル式のWalkieTalkieを作るというPOCだと書いてあります。昔ガキトラと呼ばれた子供用のおもちゃトランシーバで遊んだことを思い出しました。時代が進みそんなおもちゃをデジタル技術で手作りできるワクワクを感じさせるリポジトリです。

 面白そうなので試したいと思い、売れ残りのSTM32F407G-DISC1を購入しました。このボードはNucleoシリーズができる前からあると記憶しております。今となっては流通量が少ないみたいです。とりあえず試すためのボードはこれで良しとして、問題は無線モジュールです。対応している無線モジュールを見ると技適が無いものばかりです。とりあえずは部品箱に眠っているXBeeあたりで試すことにします。

 XBeeを使うのであれば 日本で使えないRadioHeadフォルダー以下は捨ててしまっても良さそうなのと、プロジェクトコードは最近STが推しているCubeMXで作られていないようなので、入出力部を含むメインは新規に作り直すことにしました。まずは音声の入出力部をCubeMXで定義したのですが、いろいろハマったので記録として残しておきます。プロジェクトの作成はSTM32F407G-DISC1を選択すれば、大方の設定は自動で行われるのでこれを選びました。

 最初にDACへのデータ出力を定義します。Codec2WalkieTalkieのソースコードのコメントより20msに160サンプルを扱うということなので非圧縮部のデータレートは8kHzです。

 I2S3を以下のように設定しました。

 送信側のDMAを割り当てます。モードはCirculer、Data WidthはHalf Wordとします。

 次にMEMSマイクのインタフェースを設定します。この設定はハマりました。結果は以下のようになります。同時に受信側のDMAを割り当てます。モードはCirculer、Data WidthはHalf Wordとします。

 

 設定のポイントはオーディオ周波数(32kHz)です。STM32F407G-DISC1のMEMSマイク(MP45DT02)のクロック周波数は最低でも1MHzにする必要があります。それ以下の場合はPDMの出力がありません。最低1MHzの仕様も見落としてハマりました。したがって、クロック出力が1MHz以上になるようにオーディオ周波数を設定する必要があります。で、なぜ32kHzなのか?

 STM32F407G-DISC1のマイクは1つでモノラルです。一方、I2SはLR交互にデータが並ぶステレオを前提とした作りになっています。オーディオ周波数=32kHzでフレームフォーマット=16bitを設定すると発生するクロックは、32 x 16 x 2(ステレオ) = 1024kHzとなります。I2Sはステレオ仕様ですが読み込まれたデータはLRに関係なく時系列に並びますので純粋に1024kHzのモノラルなPDMデータを取得できることになります。

 DACの使い方はこちらを参考にしました。

github.com 上記サンプルのようにDMAにメモリさえ割り当ててしまえば、適切なタイミングでデータをコピーするだけで音声を出力…と思ったのですがDMAが動いてくれません。普段はこんなことないのに何で??デバッグプリントでNDTRレジスタを確認しても値に変化がない状態です。しばし途方に暮れましたが、同じ現象を乗り切ったVLOGのおかげで解決しました。CubeMXの闇を見た感じです。

qiita.com DMAのバッファリングサイクルをLEDの点滅で確認できるように125ms分と決めました。マイク側は1024kHz / 16bit = 64kHz(15.6us)単位にNDTRレジスタがカウントされます。したがって、uint16_tの配列の長さは8000(8000 x 15.6us = 125ms)となります。DAC側は8kHz x 2(ステレオ) = 16kHz(62.5us)単位にNDTRレジスタがカウントされます。したがってuint16_tの配列の長さは2000(2000 x 62.5us = 125ms)となります。

 いよいよループバックテストのメインロジックが書けるようになりました。メインループでマイク側のNDTRレジスタをポーリングし、1データ受信したらi2s_dma_word_rx()関数を呼び出します。この関数の呼び出しタイミングは64kHz程度になる予定、出力側は8kHzなので1/8にデシメーションし8kHzを作ります。ここはCodec2WalkieTalkieとほぼ同じ処理です。少々乱暴にも見えますが8kHzのタイミングでFIRフィルタ後の値を取得し、そのままDACのDMAバッファに値を代入します。入出力ともに正常動作すれば8kHzタイミングで同期されているので特にFIFOなどを挟む必要はないはずです。

 ループバックテストは目論見通りに動作したので今回はここまでです。いろいろハマってCodec2まで到達しませんでしたが続きを楽しみたいと思います。

 

STM32でSDI-12

 ワンワイヤーのシリアル通信規格のSDI-12をSTM32につなげたいお話です。屋外設置の市販センサーに使われている規格で、 仕様書(sdi-12.org)によると初版が1988年となっていますのでそこそこ古いことが分かります。

f:id:jun930:20220417101110p:plain

通信速度とフレームフォーマット

 通信の仕様は、1200bps, 7ビット, 偶数パリティ, ストップビット1と普通のシリアル通信でOKそうですが、

f:id:jun930:20220417101411p:plain

シリアルデータライン

 信号の電圧が通常のTTLシリアルと反転しているうえに、spacingが3.5~5.5(V)となっており3.3V系のCPUには直接接続できないです。

f:id:jun930:20220417101842p:plain

 更に、コマンドを送る前に少なくとも12msecのブレーク信号を出せと書いてあります。

 これに関してOSSを探してみるとArduinoのライブラリが何種類かヒットしました。コードを読んでみると。タイマーと割り込み使って送受信しています。

github.com ATMega328Pはシリアルが一個なのでこの実装で納得ですし、5V系のArduinoでしたらピンに直結できることが本当に楽で、手持ちのArduinoで試してみたところあっさり通信できました。

 一方STM32の場合はシリアルが多チャネルあります。今回実装しようとしているSTM32L476はペリフェラルの機能としてシリアル信号を反転させこともでき、GPタイマを使う必要もなさそうなのでArduinoの実装例はあまり参考にならないと思いました。

 手始めにペリフェラルでシリアル信号を反転させてお決まりの双方向レベル変換回路を使おうと考えてみたのですが、やってみたら動きませんでした。

f:id:jun930:20220417104427p:plain

お決まりのレベル変換回路

 この回路はセンサー側が受信状態(ハイインピーダンス)のとき5Vが出続けますので使えません。ここで高価な双方向レベル変換ICを使うことに負けを感じたことをきっかけに、Arduinoのように直結とまでは行かないまでもなるべく簡易な回路にならないものか?と思い始めました。

f:id:jun930:20220417105114p:plain

仕様書のおすすめ回路

 仕様書では送信側は3ステートバッファを使えとなっていますが、今日は虫の居所が悪いのか、これも気に食わない。CPU側TX,RXの2線を使うのであれば、送受信の切り替えなどしなくて良い気がするのです。

 なるべく簡単に制作することを念頭に使用する部品も秋月電子さんで入手可能なもので作ることにしました。

f:id:jun930:20220417111358p:plain

STM32用簡易接続回路1

 送信側はインバータ、受信側はFETで信号の反転を行うことにしました。送受信の切り替えを行わないため送信側の出力インピーダンスを大きく(10kΩ)します。使用するセンサーの出力インピーダンスより十分大きくすることで、受信用FETのゲートでセンサー出力が優先されるようにしました。お決まりレベル変換の失敗を経てこの回路はうまく動きました。

f:id:jun930:20220417113345p:plain

 受信側に送信コマンドのエコーとリザルトが順次返ってきます。このような場合でも、DMAを設定するだけで通信中に受信処理をしなくても取りこぼしが起きないところがSTM32の良いところだと思います。

 ここで、ブレイク信号の送信についてです。

f:id:jun930:20220417114348p:plain

 STM32のHALにHAL_LIN_SendBreak()なる関数があり、これを呼ぶとシリアルにブレイク信号を出すことができますが、ロジアナで信号を見ると8msec強しかありませんでした。手持ちのセンサーは動作するものの仕様的には最低でも12msecですので、UARTに割り当てたピンを一度GPIOにしてLOW出力、少し待ってピンをUARTに戻す方がお行儀が良いと思いました。


 STM32のシリアルは送受信のピンを別々に反転することができます。STM32用簡易接続回路1では受信側をFETで反転していましたが、受信のピンを反転すれば抵抗に置き換えることもできそうです。

f:id:jun930:20220417115033p:plain

STM32用簡易接続回路2(受信側反転なし)

 これも同様に動作しました。これであれば、実装面積も少ないし普通のシリアル通信とほぼ同じ使い勝手のコードで納得したので一連の実験はこれで終わりです。

 STM32にSDI-12のセンサーを接続するには、秋月電子さんで販売されているインバータ(TC7S14F)一個と抵抗3つで出来ることが分かりました。

 

STM32F103で2つのタイマーを同時にスタートするメモ

 STM32のタイマにはトリガーなる機能があり、これを使うと異なるタイマを関連付けて動作させることができます。以下の例は、TIM1を10kHz、TIM2を5kHzとして両タイマをPWM出力し、エッジを同期させるための設定のメモになります。

f:id:jun930:20210503231915p:plain

■STM32CubeMX5 TIM1の設定

f:id:jun930:20210503232223p:plain

■STM32CubeMX5 TIM2の設定

f:id:jun930:20210503232321p:plain

 

 TIM2スタート後、適当なディレーを入れてTIM1をスタートさせる。delayに影響されず両タイマのエッジがそろっていればOK。

 

電波時計のアクティブアンテナを作る

 我が家の電波時計は室内でほとんど受信できません。液晶に電波マークは表示されるものの時刻を受信することはありません。そんな環境にある電波時計はただのクォーツ時計です。本当に電波が弱いのか電波時計の性能が悪いのか?と考えたら電波を見たくなってきました。昔秋月で購入した電波時計用のアンテナがあったことを思い出したので、電波時計用のアクティブアンテナを自作していきます。

 手元のアンテナは付属のコンデンサが0.012uFで60kHzに共振するものです。40kHzを受信したいので0.027uF付近にするため、在庫のフィルムコンデンサを組み合わせて0.0266uFを作りました。ファンクションジェネレータとオシロを使って共振周波数が40kHzになったこを確認しました。

 次にアンテナモジュールに内蔵するプリアンプを作ります。初段をJFET、2段目をBJTで構成することにしました。

f:id:jun930:20210314205754p:plain

 上の回路の前に初段をゼロバイアス(R2,C2抜きの回路)で試作しオシロに接続してみましたがそれっぽい信号が見えずでした。横着をせずにバイアスありの回路で作ることにしました。ゲートにはアンテナの並列共振回路が接続されますので0V、手元の2SK208-GRのIdssを計測したところ3.5mA程度なので、以下のId-Vgs特性から、Idを1mAと決めたときVgsはちょうど-1VくらいなのでR2を1kΩと決めました。

f:id:jun930:20210314211339p:plain

 電源は少し低めの3.3Vの動作を考慮し1V程度の電圧降下を見込んでR1は切りの良い1kΩとすると、自動的に2段目のバイアスが決まります。それに合わせてR3,4を決定しました。利得は28dB程度になりました。

 しかし信号を見るにはプリアンプだけでは利得不足です。後段のRFアンプを追加します。オーソドックスなエミッタ接地の増幅回路です。これで29dBくらいです。f:id:jun930:20210314212308p:plain

 これらを組み合わせ窓際で受信を試みますが、ほぼノイズしか見えません。微かにノイズのトップが1秒周期でピクついているように見受けられる、そんな気がするレベルの動きです。

 どうやら強力なフィルタが必要そうですので、プリアンプの後段にクリスタルフィルタを追加することにしました。45pFのトリマコンデンサと40kHzのクリスタルを直列に接続して、JFETのアンプで受けるようにしました。

f:id:jun930:20210314214343p:plain

 クリスタルフィルタの効果は絶大です。40kHzの形がはっきり見えるようになりました。

f:id:jun930:20210314214308p:plain

 そして、JJYっぽい波形が見えるようになりました。一連の実験の結果、我が家も窓際ギリギリの位置であればJJYを受信できることがわかりました。

 以下は製作したアクティブアンテナの実装です。タッパーにバーアンテナとプリアンプを収めてRCA端子にオーディオ用のシールドケーブルを接続できるようにしました。

f:id:jun930:20210314214848j:plain

 後段のクリスタルフィルタとRFアンプアンプはミント缶に収め、同じくRCA端子で後段にチェーンできるようにしました。

f:id:jun930:20210314215052j:plain

 5mのケーブルを2本(10m)引き回しても良好に動作しています。

 ここまでの信号が得られれば、電波時計の外部アンテナにするもよし、検波してマイコンで読むもよしですから、このネタはもう少し楽しめそうです。

UARTの2分岐治具の作成

 デバイスのUART端子1つに対してPCから2本のUARTケーブルを同時に接続できる治具を作ります。
 Arduinoのようにシリアルブートローダを持っているデバイスファームウェアを開発するとき、動作確認用のターミナルソフトがCOMポートを占有していると書き込みソフトでCOMポートが開けない、その逆のパターンもあり両ソフトのCOMポートを閉じたり開いたりの操作がめんどくさいなと感じていたのがモチベーションです。

 回路は簡単です。PC側の受信はそのまま接続、PC側の送信はダイオードを使ったワイヤードORとしました。デバイス側の受信ポートがプルアップされていない場合もあるので、片方のチャネルを10kΩでプルアップしておきます。

f:id:jun930:20210116145316p:plain

 ピン配置はいつも使っているFTDIの6ピンケーブルと互換を持たせています。

f:id:jun930:20210116150424j:plain

f:id:jun930:20210116150451j:plain

 基板はこんな感じに仕上がりました。

 使ってみた感じは、ファームウェア書き込み中はターミナルソフトに化け文字が流れて、静かになったら書き込み完了したとわかります。たまたま使っている旧Freescale社の書き込みソフトは書き込み中にウインドのフォーカスが無くなるとプログレスバーが止まる不具合があり、動いているのかどうか不安だったのですが、通常は目障りな化け文字が安心感を与えてくれる結果となりました。

コテ先温度計

 数年前から温度調節のできる半田ステーションHAKKOのFX-888とFX-888Dを使っています。これまでに温度の校正を行ったことがなく、本当のコテ先温度は未確認のままでした。今回はコテ先温度計を自作します。

 コテ先温度計で検索すると以下のArduinoシールドやワンダーキットが出てきます。HAKKOの191-121 K型熱電対センサーを使用したものです。

www.switch-science.com 上記のArduinoシールドはとても良いのですがLCDを実装すると少し不安定なので基板を延長してLCDを固定したいと思いました。また、センサーのテンションを手動で調整できるように頂点のネジ穴を長穴に、また、マックス値のホールド機能を付けたときホールドした値をクリアするためのボタンを追加したいです。

 使用するICは上記と同じMAX31855とします。このICは秋月さんで単体販売されていますので入手が容易ですけど、上限電圧が3.6Vなのでシールドにした場合はGPIOの電圧を考慮する必要があります。上記のシールドはジャンパーを設定することで切り替わるようになっています。ウチには3.3V、5VのArduinoがありますので気にせずに使えたら良いなと思ていたところ、よくできている回路を見つけました。

www.adafruit.com 3.3V,5V両対応の回路は上記のモジュールを参考にしました。

f:id:jun930:20210111203921p:plain

 基板の制作を簡単にするためパターンは片面で収まるように頑張ります。上部にLCDを固定、ホールド値の解除はリセットボタンとしました。センサーのテンションを調整するためにドリルを連打します。

f:id:jun930:20210111204706p:plain

 基板をCNCで切削して完成しました。

f:id:jun930:20210111205609j:plain

 現在の温度とホールドしたMAX値を表示するようなスケッチを書きました。使ってみて思ったことは、このセンサーはコテ先を当てる位置の調整が難しいです。真ん中の金属部に当てるより、頂点に伸びる線に当てた方がそれっぽい値が出ることが多いように思えます。もう一つの問題は、はんだコテとGNDが共通なPCをArduinoに接続しているときMAX31855の熱電対がGNDに短絡したエラーが返ってきます。電池で使うかシリアルラインにアイソレータを挟む必要があります。

 作ってみるといろいろな気付きがあります。少々難はあるもののコテ先の温度が見えるようになりました。