motoh's blog

主に趣味の電子工作やプログラミングについて書いていきます

STM32マイコンで簡易オシロスコープを自作 (4) 3Dプリンタによるケースの製作

1. はじめに

「STM32マイコンで簡易オシロスコープを自作」シリーズの第4回です。

前回までで簡易オシロスコープのソフトウェアとハードウェアが完成しましたが、基板がむき出しの状態でした。今回、ようやくケースも製作できたため、簡単ですが最後にご紹介して本シリーズを締めたいと思います。

まずは市販のケースを探してみましたが、ぴったりなサイズがなかなか見つからないため、前々からやってみたいと思っていた3Dプリントに挑戦してみました。とはいえ、いきなり3Dプリンタを購入するのはためらいがあるので、インターネットで発注できる3Dプリントサービスを利用することにしました。個人でも発注できる3Dプリントサービスはいくつかありますが、今回はDMMさんのサービスを利用させていただきました。

2. 3D CADソフトの準備

3Dプリントサービスを利用するためには、3Dデータを作成するための3D CADソフトが必要です。いろいろ調べた結果、Fusion360というソフトを利用することにしました。Fusion360は初心者にも使い易く、個人利用で非商用目的であれば機能限定版を無償で利用することができます。実際に利用者も多いようで、使い方などインターネットの情報も豊富です。

Fusion360によるケースの作り方は、以下のYoutube動画で勉強させていただきました。

3. Fusion360で作った3Dデータ

こちらが作成した3Dデータの全体像です。特に凝ったことはなく、四角い箱に最低限の穴を開け、角を丸めています。本当はフタをネジで固定できるようにしたかったのですが、ネジ穴を空けたりネジ切りしたりといったことが一発でうまくいくと思えなかったので断念しました(自前の3Dプリンタがあればトライ&エラーをしてもよかったのですが)。

ケースを上から見た図です。基板を固定するための台と突起が4か所あります。

主な寸法は次の通りです。

  • ケースのサイズ: 101 x 104 x 34 mm
  • 材料の厚さ: 2mm
  • 角の丸めR: 1.5mm

4. DMM 3Dプリントサービスの発注の流れ

おおまかな流れは次の通りでした。

  1. Fusion360で作成した3DデータをSTL形式ファイル(.stl)にエクスポート。
  2. DMMのサイトでSTLファイルをアップロードすると、ほどなくして見積完了を通知するメールが届く。
  3. サイトの「マイ3Dデータ」というページで次のように注文が可能となる。

  4. サイトから注文した後は、DMM側でデータのチェックをしてから製作に入る(データのチェックがOKになるまでは注文完了とならない)。データのチェックは注文したその日のうちに完了したが、製作に入ってから自宅に届くまでは2週間ちょっとかかった。

なお、注文するときに次のように素材を選択できます。これは価格が安い順に並んでいるようです。今回は最安のエコノミーレジンで3900円でしたのでこちらを選択しました。

5. 届いたケース

最後に、届いた実物のケースをご紹介します。

組み立てた様子です。液晶パネルの下の空白がデザイン的にはちょっと寂しいですね^^;
フタもしっかりはまりました。レジンという素材の特徴ですが、仕上がり面が一般的に想像される3Dプリンタの仕上がり(積層したときの層が目立つ)よりもずっと滑らかです。

ケース内の様子です。

基板の固定もうまくはまりました。基板の穴にはめる突起は直径2.5mmという極小な円柱ですが、3Dデータの寸法にかなり忠実のようです。

USBの穴です。コネクタが干渉しないように余裕をもったサイズにしています。

液晶パネルは今までパネルの一辺の端子をピンソケットに挿しているだけの状態だったため、ぐらぐらしていて、液晶を強くタッチするとNucleoボードのリセットボタンを押してしまう状況でした。そこで、「液晶パネルを支えるパーツ」も3Dプリンタで製作しました。

液晶パネルにはこのように当たります。このようなちょっとしたパーツも手軽に作れてしまうのは3Dプリンタのいいところだと感じました。

6. おわりに

今回は3Dプリントサービスでオシロスコープのケースを製作した様子をご紹介しました。

なんてことないデザインですが、想定通り基板がぴったりはまったり、フタがぴったり閉まったりした時の自己満足感はひとしおでした。自宅に3Dプリンタが欲しくなりました。

本当は緑や赤といったカワイイ色にしたかったのですが、DMMの現在のサービスは成形物のサイズが100 x 100 x 100 mmを超えるとカラーが選べないようです。 (カラーにすると価格が跳ね上がるので、選べたとしても自分はポチれなかったかもしれません...)

本シリーズ「STM32マイコンで簡易オシロスコープを自作」は今回で最後にしたいと思います。最後まで読んでくださりありがとうございました。

STM32マイコンで簡易オシロスコープを自作 (3) ハードウェア編

1. はじめに

「STM32マイコンで簡易オシロスコープを自作」シリーズの第3回です。

mzmlab.hatenablog.com

mzmlab.hatenablog.com

前回まででオシロスコープのソフトウェアはそこそこ使えるものになりましたが、ハードウェアはブレッドボードで組んでいる状態のため、実用的ではありません。そこで、ここからは実際に使えるものを目指してハードウェアを製作していきます。 具体的には、

  • 基板上にパーツを配置し、手のひらサイズにする。
  • 前回まではマイコンのADコンバータに信号を直接入力していたため、マイコンの電源電圧3.3Vよりも高い電圧を測定できなかった。今回、入力回路を実装することで電源電圧の10倍程度まで測定できるようにする。
  • 前回まではPCのUSBから電源供給していたため単体で持ち運びができなかった。今回、バッテリー駆動を可能にすることで持ち運びできるようにする。
  • ケースに収めることで、さらに持ち運び易くする。(第4回で実施予定)

ということをしていきます。

2. 基板に配置した様子

写真のようにユニバーサル基板(95mm × 72mm)にSTM32 Nucleoボードと液晶パネルを重ねて配置することで、扱いやすいサイズに収めることができました。オシロスコープ用のプローブを取り付けられるようにBNCコネクタを採用しました。バッテリー(9V電池)もスペースに収まっています。

横から見た様子です。液晶パネルがSTM32 Nucleoボードの高さに合うように、ピンソケットを2段重ねにしています(上側のピンソケットは足の長い連結用ピンソケットです)。9V電池をON/OFFするためのスイッチも付いています。

次の写真は、BNCコネクタにBNC-バナナプラグ変換コネクタを介してみの虫クリップを取り付けた様子です。BNCコネクタなのでオシロスコープ用のプローブも取り付け可能です。

3. 入力回路

電子回路は勉強中なので、ここで紹介する入力回路は、個人的に使うには十分な、最低限の入力回路という扱いでお願いします。

入力回路は、過大電圧からマイコンを保護することと入力インピーダンスを増大させることを目的としたバッファ回路と、マイコンの電源電圧3.3Vよりも高い電圧を測定できるようにするための分圧回路で構成しました。 回路図は次の通りです。

※全体の回路図は作成していませんが、前回記事でSTM32 Nucleoボードと液晶パネルのピンアサインを表にまとめています。

3.1. バッファ回路について

バッファ回路はオペアンプを利用します。オペアンプの出力電圧は、オペアンプの駆動電圧を超えることがないため、駆動電圧にマイコンの電源と同じ3.3Vを使用することで、マイコンを過大電圧から保護します(過大電圧でオペアンプが壊れる可能性はありますが、マイコンが壊れるよりは安価な損失で済みます)。3.3Vは、STM32 Nucleoボードの+3.3V端子から取り出せます。

また、オペアンプを取り付けることで入力インピーダンスが数MΩまで増大します。入力インピーダンスが高いほうが、測定対象の内部抵抗(出力インピーダンス)による電圧降下を抑えられるので、正確な測定ができます。

今回の用途では、単電源でレール・ツー・レールのオペアンプが望ましいため、MCP6022を利用しています。

3.2. 分圧回路について

分圧回路は、2つの抵抗R1(100kΩ)とR2(10kΩ)で構成します。この場合、分圧比は、R2 / (R1 + R2) ≒ 0.09 となります。これにより、電源電圧が3.3Vのマイコンならば、36V程度まで測定することができるようになります。

R1を90kΩにすればぴったり1/10になりますが、今回はそういう必要はなかったので手元にあった適当な抵抗を使用しました。

4. バッテリー駆動

Nucleo Board STM32F401は、Vin端子に外部電源7V~12Vを供給できるので、Vin端子に9V電池をつなげられるようにしました。Vin端子に電源供給する場合のジャンパピンの設定は次の通りです。

  • JP5のピン2と3をジャンパキャップでショートする。
  • JP1のジャンパキャップを外す。

Vin端子に電源供給する場合も、PCのUSBから電源供給する場合と同じように+5V端子と+3.3V端子が機能します。+5V端子は液晶パネルへの電源供給に使用し、+3.3V端子はオペアンプの駆動電源として使用しています。

5. おわりに

前回まではブレッドボードで組んでいた回路を基板上に実装することで、手軽に扱えるオシロスコープになりました。しかし、やはり最終的にはケースに収めたいところです。いつになるかはわかりませんが、次回にご期待ください。

⇒ 2022.9.18追記
ケースも作成しました。次の記事で解説しています。

mzmlab.hatenablog.com

STM32マイコンで簡易オシロスコープを自作 (2) タッチパネル編

1. はじめに

「STM32マイコンで簡易オシロスコープを自作」の続編です。TFT液晶モジュール (MSP2807) のタッチパネルを利用してオシロスコープのいろいろな設定を変更できるようにしました。ソースコード(STM32CubeIDEプロジェクト)は前回のGithubリポジトリにコミットしています。 mzmlab.hatenablog.com

youtu.be

2. Githubリポジトリ

ronron-gh/NUCLEO_F401RE_SIMPLE_OSCILLOSCOPE

3. マイコンのピンアサイ

Nucleo Board STM32F401のピンアサインは以下の通りです。
青文字がタッチパネル用に追加した接続です。

■ Nucleo CN5 (digital)

Pin MCU pin Function 用途
D15 PB8 - MSP2807 T_DO
D14 PB9 - MSP2807 T_IRQ
GND - Ground MSP2807 GND
D13 PA5 SPI1_SCK MSP2807 SCK
D12 PA6 SPI1_MISO MSP2807 SDO(MISO)
D11 PA7 SPI1_MOSI MSP2807 SDI(MOSI)
D10 PB6 - MSP2807 CS
D9 PC7 - MSP2807 DC/RS
D8 PA9 - MSP2807 RESET

■ Nucleo CN6 (power)

Pin MCU pin Function 用途
+3V3 - 3.3V output MSP2807 LED (バックライト)
+5V - 5V output MSP2807 VCC

■ Nucleo CN8 (analog)

Pin MCU pin Function 用途
A0 PA0 ADC1_0 入力チャンネル

■ Nucleo CN9

Pin MCU pin Function 用途
D7 PA8 - MSP2807 T_CLK
D6 PB10 TIM2_CH3 テスト用信号(10kHz矩形波)
D5 PB4 TIM3_CH1 -
D4 PB5 - MSP2807 T_CS
D3 PB3 TIM2_CH2 -
D2 PA10 - MSP2807 T_DIN

4. おわりに

GUI系のプログラムはボタン等が増えていくとどんどんコード量が膨れ上がっていくので、C言語ですがオブジェクト指向的に実装して整理しやすいように工夫しました。液晶表示やタッチパネルのプログラミングは実は初めてでしたが、Githubのプログラムは興味がある方はご自由にご利用ください。

STM32マイコンで簡易オシロスコープを自作

1. 概要

電子工作をしていると、デバッグの際にモータ制御信号や通信線の信号をオシロスコープで確認したくなることがあります。簡易的なオシロスコープであれば数千円で買うこともできますが、簡易的なものであれば自分でも作れるのではないかと思い、自作に挑戦してみました。

簡易オシロスコープということで、仕様は次の通りです。

  • チャンネル数 : 1ch (立ち上がりエッジでトリガ可)
  • 周波数帯域 : 40kHz
  • A/D分解能 : 12bit
  • サンプリング周波数 : 200kS/s (※1)
  • レコード長 : 300サンプル
  • 最高入力電圧 : 3.3V (※2)

STM32マイコンの評価ボード Nucleo Board STM32F401の機能だけで上記仕様を実現しています。まだまだ荒削りですがマイコンのソフトウェアをGithubで公開したいと思います(筆者はオシロスコープの専門知識があるわけではないため、クオリティはあまり期待しないでくださいm(__)m )。
次の写真のように、ブレッドボードでソフトウェアの動作を確認できます。液晶モジュールは、ILI9341搭載タッチパネル付TFT液晶 (MSP2807) を使用しています。

youtu.be

※1 自分の用途では200kS/sで十分なのでそのように実装しましたが、ADCの性能的には1MS/s以上にすることも可能かもしれません。

※2 マイコンのADCポートに直接信号を入力しているため、マイコンの電源電圧3.3Vを超える信号は入力できません。本来は入力回路も適切に設計する必要がありますが、そこは追々勉強していきたいと思います...

2. 開発環境

  • 開発用PC ・・・ OS: Windows10 64bit
  • 統合開発環境 ・・・ STM32CubeIDE Version:1.8.0

3. Githubリポジトリ

ronron-gh/NUCLEO_F401RE_SIMPLE_OSCILLOSCOPE

4. マイコンのピンアサイ

4.1. TFT液晶モジュールとの接続

■ Nucleo CN5 (digital)

Pin MCU pin Function TFT液晶 (MSP2807)
GND - Ground GND
D13 PA5 SPI1_SCK SCK
D12 PA6 SPI1_MISO SDO(MISO)
D11 PA7 SPI1_MOSI SDI(MOSI)
D10 PB6 - CS
D9 PC7 - DC/RS
D8 PA9 - RESET

■ Nucleo CN6 (power)

Pin MCU pin Function TFT液晶 (MSP2807)
+3V3 - 3.3V output LED (バックライト)
+5V - 5V output VCC

※バックライトはとりあえず3.3Vに接続していますが、GPIOに接続すればソフトウェアでバックライトをON/OFFできます。

4.2. 入力チャンネル

■ Nucleo CN8 (analog)

Pin MCU pin Function 用途
A0 PA0 ADC1_0 入力チャンネル

4.3. テスト用信号

■ Nucleo CN9

Pin MCU pin Function 用途
D6 PB10 TIM2_CH3 テスト用信号(10kHz矩形波)

5. ソフトウェア設計概要

以下、ソフトウェア設計のポイントになる部分(苦労した部分^^;)について記載します。

5.1. サンプリング機能

5μs (200kHz)の正確な周期でAD変換し、ADデータをメモリ(RAM)に時系列に格納するにはどうすればよいかという問題です。5μs周期の処理はタイマ割り込みを使えば容易に実現できますが、割り込みルーチンの中でAD変換、メモリ格納を行っていては、5μsの内に処理が終わらないかもしれません。

そこで、次の図のように、タイマ、ADC、DMAを連携させて、サンプリングの動作をすべてハードウェアにやらせることにします。すなわち、タイマー割り込みをトリガにしてAD変換を起動し、AD変換終了をトリガにしてDMAでADデータをRAMに転送します。また、DMAをサーキュラモードにすることで、図のように、指定した転送数のメモリ転送を無限に繰り返すことができます(繰り返しにより、先頭データから上書きされていく)。

5.2. トリガ機能

トリガ機能とは、入力信号が閾値を超えたらサンプリング(もしくは画面更新)を停止する機能です。次の図のように、ADCのアナログウォッチドッグ機能(AD変換結果が閾値を超えたら割り込みを発生)とタイマを利用して実現しました。

5.3. グラフ表示機能

サンプリングデータをリアルタイムにTFT液晶モジュール (MSP2807)にグラフ表示します。 ドライバIC (ILI9341) のインタフェースはSPIですが、MSP2807のサンプルプログラムをそのまま使うと、ソフトウェアで1ドットずつRGBデータをSPI送信データレジスタに書き込むため、ディスプレイの更新が非常に遅くなります(ディスプレイが上側から下側に更新されていくのが目で見える程遅いです)。そのため、全体(320x240)のRGBデータを一旦RAM上で作成し、DMAでSPI送信データレジスタに転送するようにして高速化しています。

6. 今後の展望

(1) TFT液晶MSP2807はタッチパネル搭載なので、タッチ操作でいろいろな設定を変更できるようにしたい。
⇒ 2022.06.19 追記
タッチパネルに対応しました。次の記事で解説しています。

mzmlab.hatenablog.com

(2) 入力回路をきちんと設計して、ケースに入れて、本当に使えるものにしたい。
⇒ 2022.08.21 追記
基板上に実装し、入力回路も作成することで実用的になりました。次の記事で解説しています。

mzmlab.hatenablog.com

⇒ 2022.09.18 追記
ケースも作成しました。次の記事で解説しています。 mzmlab.hatenablog.com

micro:bitの各種BLEサービスを試せるAndroidアプリを作ってみた

最近、興味本位でmicro:bitを購入しました。micro:bitは、小さな基板上に温度、加速度、磁力といったセンサを搭載しており、それらセンサ情報をBluetooth Low Energy (BLE)のサービスで他のデバイスに提供することができます。また、UARTサービスを利用すれば、BLE経由でmicro:bitとシリアル通信することができます。今回は、Androidスマートフォンからmicro:bitの各種BLEサービスを試すために作成したアプリを紹介したいと思います。

AndroidアプリのソースはこちらGithubで公開します。本記事の中でソースの解説はしませんが、ソースにはある程度コメントは入れてあります。また、アプリを作成するにあたって参考にさせていただいたサイトを最後に紹介します。

尚、micro:bitのBLEサービスをただ試すだけなら、Google Playで公開されているnRF Connect等のアプリを使った方が簡単です。今回は、自作アプリでmicro:bitと通信できれば、今後おもしろいものが作れるかもしれないと思い、あえて自作にチャレンジしました。BLEの基本的な仕組みについての良い勉強にもなり、micro:bit以外にも応用ができそうです。

動作環境

Android Studio 3.1.4
実機: S7-SH(Android 11)

micro:bit側のプログラム

micro:bitでBLEを使うためには、まずMake Codeのプロジェクトの設定を変更する必要があります。
こちらのサイトでとてもわかりやすく解説されています。
monomonotech.jp

プログラムはお好みで構いませんが、私は次のように作りました。

”最初だけ”のイベントで、利用したいBLEサービスを立ち上げます。

Bluetoothデータを受信したとき”のイベントで、受信したデータをそのまま送り返します(エコーバック)。
受信したことがわかるように、LEDにも受信データを表示します。

接続/切断したことがわかるように、接続/切断それぞれのイベントでLEDにアイコンを表示します。

アプリ仕様

f:id:mzmlab:20220120232740p:plain

f:id:mzmlab:20220120232755p:plain

f:id:mzmlab:20220120232810p:plain

f:id:mzmlab:20220120232821p:plain

f:id:mzmlab:20220120232833p:plain

f:id:mzmlab:20220120232844p:plain

参考にさせていただいたサイト

BLEを使ってAndroidとマイクロビットをつなげるまで(1) | ECF Tech
周辺のBLEデバイスをスキャンしてmicro:bitを見つけるところから、センサの値を取得するところまで丁寧に解説されており、とても参考になりました。

04.BLEデバイスから値を読み込む処理を作る
先にご紹介したサイトは、Notificationモード(micro:bitから一定周期でセンサデータを通知するモード)を利用していますが、Androidアプリから要求したとき(ボタンを押したとき)だけmicro:bitからセンサデータが返信されるようにする場合の処理はこちらを参考にしています。

06.BLEデバイスへ値を書き込む処理を作る
micro:bitのUARTサービスに文字列を送信する処理は、こちらを参考にしています。

ハマりかけたこと

初めの頃、デバイススキャンでmicro:bitを発見しサービスを取得したときに、温度計サービスや加速度計サービスが取得できない事象が発生しました(その他のデフォルトで立ち上がるサービス(Generic Access 等)は取得できる)。既成アプリのnRF Connectを使っても同じ事象だったので、私のスマホ固有の事象かもしれないと諦めかけましたが、ダメ元でスマホmicro:bitのペアリング(A、Bボタンとリセットボタンを同時に押すやつ)を実行してから再トライすると、無事に温度計サービスや加速度計サービスを取得できました。その後は、ペアリングしなくても問題なくサービスを取得できています。ペアリングはその時が初めてではなかったので、本当にそのおかげで解決したのかは不明です・・・

Android/MobileFFmpegによる動画ファイル圧縮

スマホのカメラで撮影した動画ファイルを、スマホ内でFFmpegを使って圧縮できれば便利なアプリが作れそうだと思い、方法を調べました。MobileFFmpeg(Github)がAndroidに対応しており、使い方はWeb上に多数の情報がありますが、いざその通り使ってみると画質の劣化が気になったため、その解決策も含めて解説したいと思います。

動作環境

Android Studio 3.1.4
実機: S7-SH(Android 11)

ソースコード

以下のJavaソースコードは、実行ボタンを押すとスマホ内に保存されている特定の動画ファイルをMobileFFmpegで圧縮し、アプリ固有のフォルダ "Android/data/(パッケージ名)" に保存するという単純なアプリです。圧縮中は、ダイアログに進捗が表示されます。

■AndroidManifest.xml
スマホ内のストレージにアクセスするために以下の権限を追加します。

   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

なお、私のスマホS7-SH(Android 11)だとこれだけでは不十分で、アプリをインストールした後に、スマホの設定⇒プライバシー⇒権限マネージャーで、「ファイルとメディア」へのアクセスをアプリに許可する必要がありました。
以下の記事で解説されているように、アプリ実行時にユーザに許可を求める方法もありますが、上手くいかなかったので諦めてしまいました^^;
Androidの実行時パーミッションチェックを実装する - Qiita


■build.gradle
MobileFFmpegを使うために、dependenciesに以下の行を追加します。

    dependencies {
        implementation 'com.arthenica:mobile-ffmpeg-full-gpl:4.4'
    }

■MainActivity.java
簡単ですが、コメントで説明を記載しています。

importの部分は省略

public class MainActivity extends AppCompatActivity {
    public String path_out;
    public String path_in;
    public String filename;
    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

   }

    public void onButtonExecuteArchive(View view){
        TextView textView = findViewById(R.id.textView);

        filename = "211010_140246.mp4";     //圧縮対象のファイル名

        try {
            //圧縮対象の動画ファイルの絶対パスを取得
            // (/storage/emulated/0/DCIM/100SHARP/211010_140246.mp4のようなパスになる)
            File dcimDir = new File(Environment.getExternalStorageDirectory() + "/" + Environment.DIRECTORY_DCIM + "/100SHARP");
            path_in = dcimDir.getAbsolutePath() + "/" + filename;

            //圧縮後の動画ファイル保存先の絶対パスを取得
            // (/storage/emulated/0/Android/data/(パッケージ名)/211010_140246.mp4のようなパスになる)
            File myDir = new File(Environment.getExternalStorageDirectory() + "/Android/data/com.example.memoriesarchiver");
            path_out = myDir.getAbsolutePath() + "/" + filename;

            textView.setText("FFmpeg execution start.\n");
            textView.append("input : \n" + path_in + "\n");
            textView.append("output : \n" + path_out + "\n");

            //////動画圧縮の進捗を表示するためのprogressDialogを表示する//////
            //progressDialogのMAXを設定するために動画ファイルの情報を取得する(動画の総時間をMAXとする)
            MediaInformation info = FFprobe.getMediaInformation(path_in);

            //progressDialogを表示する
            progressDialog = new ProgressDialog(this);
            progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            progressDialog.setTitle("FFmpeg progress");
            progressDialog.setMax((int) (Double.parseDouble(info.getDuration()) * 1000));
            progressDialog.setIndeterminate(false);
            progressDialog.setCancelable(false);
            progressDialog.show();

            //////動画圧縮を開始する(非同期で実行されるため、圧縮完了する前に処理が戻り、画面操作可能となる)//////
            long executionId = FFmpeg.executeAsync("-i " + path_in + " -crf 30 -c:v libx264 -c:a aac " + path_out, new ExecuteCallback() {

                @Override
                public void apply(final long executionId, final int returnCode) {
                    TextView textView = findViewById(R.id.textView);
                    if (returnCode == RETURN_CODE_SUCCESS) {
                        progressDialog.dismiss();
                        textView.append("FFmpeg command execution completed successfully.\n");
                    } else if (returnCode == RETURN_CODE_CANCEL) {
                        textView.append("FFmpeg command execution cancelled by user.\n");
                    } else {
                        textView.append("FFmpeg command execution failed.\n");
                    }
                }
            });

            //圧縮の進捗が更新されるたびに呼ばれる処理。ここでprogressDialogの進捗を更新する
            Config.enableStatisticsCallback(new StatisticsCallback() {
                public void apply(Statistics newStatistics) {
                    progressDialog.setProgress(newStatistics.getTime());
                }
            });
        }
        catch(Exception e){
            textView.append("Exception : " + e.toString());
        }
    }
}

スクリーンショット

①アプリ起動したときの画面
 Executeボタンで圧縮を実行

②実行ボタン押下後の画面
 動画の圧縮が始まり、進捗ダイアログが表示される

③圧縮終了後の画面
 圧縮が正常に終了すると"FFmpeg command execution completed successfully.”と表示される

f:id:mzmlab:20211018003719p:plain

圧縮後の画質について

MobileFFmpeg公式のGithubでは以下の使い方が紹介されていますが、このままでは圧縮後の画質の劣化がちょっと気になります。

    int rc = FFmpeg.execute("-i file1.mp4 -c:v mpeg4 file2.mp4");

画質の劣化を抑えるには、コーデックをH.264にすると良いみたいです。以下のように変更します。

    int rc = FFmpeg.execute("-i file1.mp4 -c:v libx264 file2.mp4");

libx264を使うには、gradleで指定するMobileFFmpegパッケージは”full-gpl”を選択する必要があります。(公式Githubの解説で、パッケージ毎の対応ライブラリが表でまとめられています。)

    dependencies {
        implementation 'com.arthenica:mobile-ffmpeg-full-gpl:4.4'
    }

※ “full-gpl”はGPLライセンスなので、ソフトウェアを頒布する場合はソースコードの公開義務が発生します。

また、以下のように-crfで圧縮の品質を変更できます。私の環境では-crf 30だと画質の劣化が見た目にはほとんどわからず、ファイルサイズは3分の1程度に圧縮できました。-crfの数字が小さいほど圧縮率は下がり、画質は良くなります。

    int rc = FFmpeg.execute("-i file1.mp4 -crf 30 -c:v libx264 file2.mp4");

マイコン&距離センサでプラレールの自動ブレーキ制御

2歳の子どもが遊ぶプラレールが、しばしば前方車両や壁に衝突するのをみて(そもそも対象年齢じゃないのですが^^;)、自動で減速するように改造できないかと思っていたところに以下の記事を発見。電子工作は経験が浅いのですが、自分でもトライしてみました。

tandh.work

製作物紹介(使用部品、回路図)

先の記事の例に倣って先頭車両の単2電池の代わりに単4電池2本と電子回路を収めました。マイコンは手元にあったATtiny2313、赤外線距離センサは最大10cmまで計測可能なVL6180Xを搭載したセンサモジュールを選びました。マイコンと距離センサはI2Cで接続します。電源はDC-DCコンバータで電池の3V(充電式なら2.4V)を3.3Vに昇圧してマイコンとセンサに供給し、モータードライバICには電池から直接供給します。

 

f:id:mzmlab:20210718084102j:plain

少しわかりづらいですが、フロントのセンサ部分をくり抜いています。

 

f:id:mzmlab:20210718084156j:plain

基板1枚には収まらなかったため、マイコンの上側にモータドライバICをスタックしています。

ATtiny2313はプログラム書き込み用のポートとI2Cポートが共用になっているため、距離センサはコネクタで取り外しできるようにしました。

 

回路図です。 

f:id:mzmlab:20210718084308p:plain

動画

youtu.be

動いている様子です。前方車両との接近を検出して減速しています(動画の後半は制御が間に合わず衝突してしまっているので、ソフトは改善の余地がありそうです)。なお、PWM制御により、モータにかかる電圧は1.5Vに制限しています。制限を外せばMAX 3Vまで上げられるので、カーブで脱線するくらい高速で走ることもできます(※モータの定格を超えるので壊れるかもしれません)。

 

以下のような応用も楽しそうですね。

・せっかくHブリッジなので、壁を検出するなどしたら後進させてみる。

・距離センサVL6180Xは環境光も測れるので、部屋が暗くなったらLEDを点灯する。

Bluetoothモジュールを搭載してスマホアプリから遠隔操縦できるようにする。

 

苦労したこと

・単2電池のスペースはそこそこ広く見えますが、実際やってみると電池ボックスと回路を収めるのにとても苦労しました。高さがぎりぎりで、部品を出来る限り低く付けたり電池ボックスの底を削ったりしてなんとか収めました。

 

・単4電池2本(3V)ですべての電源を賄うのも、初めてだったので少し迷走しました。最初に選定したモータドライバICTB67H450FNG)が4.5V以上でないと動作しないことに後で気付き、低電圧動作するNJU7386を選びなおしました。

 

・ATtiny2313フラッシュメモリ2Kバイト、RAM 128バイトという制限が、これまた思いの外厳しいです。デバッグ用のコードをいろいろ入れたせいでもありますが、すでにフラッシュメモリのプログラム領域は97%を使用しています。RAMも小さいため、スタックのサイズに気を付けないと、関数を入れ子にしたり割り込みをかけたりしたときにプログラムが暴走しました。もし同じことをしてみたいという方がいらっしゃれば、もう少し高スペックなマイコンを選んでもよいかもしれません。

 

このように思いの外苦労したので、2歳の怪獣に壊されるのが惜しくなってきました()。見せてあげるのはもう少し対象年齢に近づいてからにしたいと思います…

 

参考情報

ATtiny2313の開発環境構築

こちらで一通り解説してくださっており、迷わずプログラム書き込みまでできました。

なお、開発環境Microchip Studioは新しくなっているのでこちらを参考にダウンロード、インストールしました。

なお、今回はFuseは出荷時のままで問題ありません。間違って書き換えると動作しなくなったりするのでご注意ください。

 

ATtiny2313I2C Masterの実装

ATtiny2313SCLSDAのポートがありますが、ハードウェアは最低限の機能しかなく、ソフトウェアでI2Cを実装する必要があります(自分も作りながら気付きました...)。ネットでいろいろ調べてみると、本家のサンプルコードがあったのでこちらを利用しました。

アプリケーションノート

サンプルコード

サンプルコードはサイト内で”I2C”で検索して出てくる”AVR310 USI as I2C master”をダウンロードし、その中のUSI_TWI_Master.cUSI_TWI_Master.hを自分のプロジェクトに追加すると使えました。