Pythonでは簡単に行列計算が出来るのに、Arduinoでは行列計算をさせるのに一苦労。そんなときはArduinoEigenライブラリの使用がおすすめです。今回は、ArduinoEigenのインストールから使い方まで解説します。
C++には行列計算ライブラリ「Eigen」というものがあります。このEigenをArduinoで直接扱えるようにしたのが「ArduinoEigen」です。ArduinoEigenをインストールすることによって、Arduinoでも行列が簡単に計算できるようになります。
ArduinoEigenのインストール方法はとっても簡単です。ここではArduino IDEとPlatformIOでのインストール方法を紹介します。
スケッチ > ライブラリをインクルード > ライブラリを管理 をクリックします。
Eigenを検索し、インストールボタンを押せばインストール完了です。
本ブログでは、Arduino開発環境はPlatformIOをお薦めしています。
PlatformIO環境でのArduinoEigenインストール方法も解説していきますね。
Librariesから「Eigen」と検索すると「ArduinoEigen」がヒットします。
Add to Projectボタンを押し、Projectを選択するとインストール完了です。
ArduinoEigenの基本的な使い方について解説していきます。詳細な使い方を知りたい方は、公式ホームページを見て下さいね。
まずはヘッダファイルのインクルードが必要です。ArduinoEigen.hをインクルードしましょう。
#include <ArduinoEigen.h>
変数を宣言するには下のように記述します。
using Eigen::MatrixXd;
MatrixXd m(2, 2);
MatrixXd m(2, 2)は、2×2のdouble型で変数mを宣言したことを意味しています。
正方行列で次数が決まっている場合は、次のように記述します。
using Eigen::Matrix2d;
Matrix2d m;
変数の型をdoubleではなくfloat、intにしたい場合は最後の文字を「d」から「f」、「i」に変更します。
MatrixXd // double型
MatrixXf // float型
MatrixXi // int型
初期値を代入するには、2×2行列の場合を例にすると1行1列目、1行2列目、2行1列目、2行2列目の順に「,」区切りで記述していきます。
m << 1, 2,
3, 4;
加算は「+」演算子を使います。
m = m1 + m2;
減算は「-」演算子を使います。
m = m1 - m2;
乗算は「*」演算子を使います。
m = m1 * m2;
転置行列はinverse()関数を使います。
n = m.inverse();
Arduinoで行列を扱うためのArduinoEigenライブラリについて解説しました。今回の解説は行列計算のごく一部です。その他行列計算の使い方は公式ホームページ参照して下さい。
最後までお読みいただきありがとうございました。
]]>制御の醍醐味は不安定な系を安定にさせること。今回は、M5StickCを使って倒立振子に挑戦します!
制御工学を学んでいるけど、数式が多くて難しい、何の役に立つのか分からないといった方の助けになればよいと思います。
倒立振子を立たせるのに、かなり試行錯誤しました。どんなところに注意しなければいけないかポイントも紹介していきます。
記事は、ハードウェア設計編、ソフトウエア設計編、実機適合編に分けて連載する予定で、今回はハードウェア設計編です。
準備した部品の一覧表です。
準備物 | 個数 |
---|---|
M5StickC Plus | 1個 |
FEETECH ギヤードモータ FM90 | 2個 |
タイヤ FS90R対応 | 2個 |
DRV8835使用ステッピング&DCモータドライバモジュール | 1個 |
電池ボックス 単3×2本 | 2個 |
ボディ(3Dプリンタで自作) | 1個 |
ジャンパピン メス | 必要分 |
GROVE 4ピンケーブル | 1本 |
コントローラはM5StickC Plusを使いました。M5StickCPlusは、小型軽量ながら倒立振子に必要なGセンサ、ジャイロセンサーが備わっているので、部品点数削減に繋がります。あとはモーター、タイヤと電源を準備するだけでOK。
私はM5StickCシリーズの中でもM5StickC Plusを使用しました。M5StickC Plus選定は在庫があったからだけです。より安価に作るのであればM5StickCでも問題ないです。
マブチモーターより小型なFM90を選定しました。FM90はサーボモーターと同一形状のため、サーボモーター用のタイヤを取り付けることができます。
定格電圧は4.8V。モーター特性は秋月電子のホームページを参照してください。
タイヤはFS90R用を使いました。FM90にも取付可能です。タイヤ径は60mm。
モータードライバは、2ch内蔵のモジュールであるDRV8835を使います。15mm×10mmと小型で、搭載性抜群です。
FM90は定格電圧4.8Vのため、充電式単3電池4本使います。重量バランスを考えて、電池2本ずつ前後に搭載することにしたので、2本用を2個用意しました。
配線図は下図のとおりです。
DRV8835への電源供給のために、単3電池の+端子をDRV8835のVM、VCC端子に繋ぎます。単3電池のー端子は、DRV8835のGNDに繋ぎます。
モーターFM90への接続は、それぞれAOUT1/AOUT2、BOUT1/BOUT2です。モーターは左右で反転して搭載しますので、一方のモーターをプラスマイナス逆転して接続しましょう。
モーターをPWM制御するために、G33端子をAIN1/BIN1、G32端子をAIN2/BIN2に接続しました。
ボディはFusion360で設計しました。
これを3Dプリンタで製作。一体成型です。3Dプリントの設定はサポート材有り。サポート材を取り除くのにかなり苦労しました。
モーターは下方から差し込む構造になっています。
電池ボックスを取り付けるためのネジ穴はボディに空けなかったので、両面テープで貼り付けます。配線は中央の穴を通しました。
タイヤをモーターに取り付け。
モータードライバDRV8835は電池パックに両面テープで貼り付けました。DRV8835のヘッダピンにそれぞれの配線を接続。
DRV8835は剥き出しになってしまったので、もう少し見栄えを良くしたかった。
M5StackC Plusをボディに差し込めば完成!
これにてハードウェア設計編は終わりです。次回はソフトウェア設計編です。
]]>モーター制御などの組み込みシステムでは、一定の制御周期でプログラムを実行するタイマ割り込みが必要不可欠ですよね。
今回の記事では、M5Stackでタイマ割り込みを実装する方法について解説します。
なお、排他制御など難しい処理は扱わず、分かりやすいよう最低限の実装に留めました。
それでは始めていきましょう!
この記事はこんな人におすすめ!
タイマ割り込みは、一定の周期でプログラムを実行したいときに用います。
今回のゴールは、図のようにシリアル通信で定期的に情報を送信するプログラムの作成です。
マイコンは動作クロック周波数で高速に動作します。
すぐに処理されてしまう簡単なプログラムを1秒間隔で動作させたいとき、どのようにしますか?
メインループ内に1秒待つ処理を加えるのも1つの手です。
でも、タイマ割り込み機能を使えば、待ち時間を作る必要はないです。
タイマ割り込みプログラムをいきなり見ても何をどうしたいのか、さっぱり分からないと思います。プログラムを理解するにはタイマ割り込みの知識が必要不可欠です。
そこでタイマ割り込みについて簡単に説明します。
タイマ割り込みは簡単です。
マイコンが持つタイマ機能が時間をカウントします。
所定の時間になったらメインの処理を一時中断して別の処理をしてねと、タイマ機能からCPUへ割り込み要求を出させます。別の処理が実行し終わったら、中断していたメインの処理に戻します。
割り込みサービス・ルーチンは割り込み関数のことです。
タイマ機能が割り込みを要求したときに実行する処理を記述します。
割り込みサービスルーチンを実行する際は、メイン処理ループを一時中断。中断したメイン処理ループのアドレスは忘れないようスタックに記憶しておきます。
割り込みサービス・ルーチンが終了したら、スタックから中断したメイン処理ループのアドレスを取り出し、メイン処理ループを再開します。
タイマ割り込みが発生したときに実行する割り込みサービス・ルーチンを対応付けた表を割り込みベクタ・テーブルといいます。
割り込みベクタ・テーブルには、割り込みサービス・ルーチンの先頭アドレスが書かれます。
タイマ割り込みプログラムを書く前に、M5Stackのタイマ機能を調べておきましょう。
M5StackのマイコンはESP32が使用されています。ESP32のタイマ数は「4」です。プログラムでは0~3を指定します。
M5Stackのペリフェラル周波数は80MHzです。
タイマのカウント周波数は、分周器というペリフェラル周波数を整数分の1に落とす装置で作り出します。タイマカウントしやすいように分周比を80とし、1カウントを80M / 80 = 1MHzにするのが一般的です。
ここまでタイマ割り込みの仕組み、M5Stackのタイマ機能について説明してきました。これらを理解していれば、タイマ割り込みサンプルプログラムの意味がきっと分かると思います。
▼は、0.1s毎にシリアル通信でプログラム経過時間を送信するサンプルプログラムです。
#include<M5Stack.h>
hw_timer_t * timer = NULL;
volatile uint32_t counter = 0;
volatile uint32_t current_time = 0;
void onTimer(){
counter++;
current_time = millis();
Serial.printf("No. %u, %u ms\n", counter, current_time);
}
void setup() {
M5.begin();
Serial.begin(115200);
// タイマ作成
timer = timerBegin(0, 80, true);
// タイマ割り込みサービス・ルーチン onTimer を登録
timerAttachInterrupt(timer, &onTimer, true);
// 割り込みタイミング(ms)の設定
timerAlarmWrite(timer, 100000, true);
// タイマ有効化
timerAlarmEnable(timer);
}
void loop() {
}
プログラムは、まず割り込みサービス・ルーチン onTimerを定義します。そして、タイマ作成→タイマ割り込みサービス・ルーチンの登録→割り込みタイミングの設定→タイマ有効化といった流れです。
このプログラムを実行し、シリアル通信をモニタすると、0.1毎にタイマ割り込みが生じていることが確認できます。
シリアル通信のモニタ方法は▼の記事で解説しています。
https://craft-gogo.com/m5stickc-serial-pclog/
ここからは各処理について解説していきます。
タイマ割り込み時に実行する割り込みサービス・ルーチンを定義します。
void onTimer(){
/* (省略) */
}
タイマを作成するにはtimerBegin関数を呼び出します。
timer = timerBegin(0, 80, true);
第1引数は使用するタイマ番号です。0~3の値を設定しましょう。今回は0を設定しました。
第2引数は分周比です。M5Stackのペリフェラル周波数は80MHzでしたね。分周比80に設定し、1カウントを1MHzとしました。
割り込みサービス・ルーチン「onTimer」をタイマ割り込みに登録するため、timerAttachInterrupt関数を呼び出します。
timerAttachInterrupt(timer, &onTimer, true);
第1引数には、作成したタイマを指定します。第2引数は登録する割り込みサービス・ルーチンを指定します。第3引数はtrueを指定しましょう。割り込みはレベルトリガーになります。
何秒毎に割り込みが発生するか設定するため、timerAlarmWrite関数を呼び出します。
timerAlarmWrite(timer, 100000, true);
第1引数には、作成したタイマを指定します。第2引数は、割り込み発生のカウント数です。今回は0.1秒毎に割り込み発生したいので、1MHz × 0.1s = 100000回 としています。第3引数は、割り込みは周期的か1回か指定します。 trueだと周期的に割り込みが発生します。
ここまででタイマ割り込みの設定が整いました。timerAlarmEnable関数を呼び出し、タイマ割り込みを有効化しましょう。
timerAlarmEnable(timer);
引数は作成したタイマを指定します。
今回の記事では、M5Stackでタイマ割り込みを実装する方法について解説しました。
解説したことは
タイマ割り込みには、割り込み中に別の割り込みを禁止する排他制御がありますが、今回は分かりやすい最低限の実装に留めました。
これから、M5Stackでタイマ割り込みを実装する方のお役に立てれば幸いです。
最後までお読み頂きありがとうございました。
M5StickCで計測した加速度データをUSBを経由してPCでロギングする実験をしたので記事にしてみました。
PCロギングは、3種類の方法を紹介します。
1つ目はターミナルソフト TeraTermを使ったロギング。2つ目はArduino IDEのシリアルモニタ、シリアルプロッタを使ったロギング。3つ目はPythonでのロギングです。
この記事はこんな人におすすめ!
この記事のゴールはこちら▼
それでは始めていきましょう!
加速度データを送信するM5StickC側のサンプルプログラムを以下に示します。
#include <M5StickCPlus.h>
float x, y, z; // 加速度データを格納
bool isSend = false; // データ送信フラグ
void setup() {
M5.begin();
M5.IMU.Init();
Serial.begin(57600);
M5.Lcd.setRotation(3);
M5.Lcd.setCursor(0,0,4);
M5.Lcd.println("Not Send");
}
void loop() {
M5.update();
if(M5.BtnA.wasPressed()){
isSend = !isSend;
if(isSend){
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0,0,4);
M5.Lcd.println("Send");
}
else{
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0,0,4);
M5.Lcd.println("Not Send");
}
}
if(isSend){
// 加速度データ取得
M5.IMU.getAccelData(&x, &y, &z);
// 加速度データを送信
Serial.printf("%5.3f,%5.3f,%5.3f\n", x, y, z);
}
delay(200);
}
シリアル通信によるデータ送信方法はとても簡単です。
まず、Serial.begin()関数を呼び出し、シリアル通信のデータ転送速度を設定します。単位は「bps(ビット/秒)」です。
データを送信するには、Serial.printf()の引数に送信したいデータを指定するだけです。
ターミナルソフト「Tera Term」でデータ受信する方法について解説します。
Tera Termを起動したらTCP/IP、シリアルが選択できますので、シリアルを選択しましょう。
次に「設定」から「シリアルポート」を選択し、スピードをM5StickCで設定した値にします。今回のサンプルプログラムでは、57600[bps」です。
続いて改行コードの設定です。M5StickCは、改行を「\n」で指定しています。そのため、「設定」から「端末」を選択し、改行コードを「LF」に変更します。
これでTera Termの設定は完了です。M5StickのAボタン(M5と彫られたボタン)を押しましょう。
0.2秒ごとに加速度センサのデータを受信することが出来ます。
Arduino IDEにもシリアル通信する機能があります。シリアルモニタとシリアルプロッタです。
まずシリアルモニタについての解説です。
Arduino IDEを起動し、「ツール」から「シリアルモニタ」を選択して下さい。
M5StickCのAボタンを押すと、Tera Termのときと同様に加速度センサのデータを受信することが出来ます。
次にシリアルプロッタです。シリアルプロッタは受信したデータを時系列で表示することが出来ます。
「ツール」から「シリアルプロッタ」を選択して下さい。
M5StickCのAボタンを押すと、受信したデータが時系列で表示されていきます。
簡易的な機能なので、縦軸などの変更は出来ないようです。
Pythonを使ったデータ受信は、プログラムを書く必要がありますが、プログラム次第でどんな表示も出来ます。今回は、TeraTerm、Arduino IDEのシリアルモニタと同様、受信データを順に表示するようにしました。
Pythonには、シリアル通信を簡単に実装することが出来る「pySerial」モジュールがありますので、これを使いました。
import serial
import time
ser = serial.Serial("COM4", 57600, timeout=0.1)
time.sleep(2)
try:
while True:
if ser.in_waiting > 0: # シリアル通信 受信待ち
bytes = ser.readline()
result, buf = bytes.decode('utf-8').split('\n')
data = list(map(float, result.split(',')))
print("AccX: {0}, AccY: {1}, AccZ: {2}".format(data[0], data[1], data[2]))
time.sleep(0.2)
except KeyboardInterrupt:
pass
print('program end')
ser.close()
まず、serial.Serialクラスを呼び出します。第1引数にはCOMポート番号、第2引数にはデータ転送速度を設定します。
データを受信したときのみターミナル表示にしたかったので、if文の条件としてser.in_waitingが0より大きいときにしました。ser.in_waitingは受信データのバイト数を戻り値として返します。
無限ループを避けるため、「ctrl + c」でプログラムを終了するようにしました。
M5StickCのAボタンを押すと、ターミナル上に加速度データが表示されていきます。
M5StickCで計測した加速度データをUSBを経由してPCでロギングする方法について、TeraTermを使ったロギング、Arduino IDEのシリアルモニタ、シリアルプロッタを使ったロギング、Pythonでのロギングを例に挙げて解説しました。
TeraTerm、Arduino IDEはプログラムを書く必要が無く、手軽にデータロギングすることが出来ます。
一方、Pythonは自分でプログラムを書く必要がありますが、matplotlibなどのグラフ描画ライブラリを使ってグラフ化したり、csvにデータを書き出すなど応用が効くと思います。
以上、今回の記事が皆さんの参考になりましたら幸いです。
]]>テレワーク中に家族が部屋に入ってきて、Webカメラに映りこんでしまった、音声が入りこんでしまったトラブルってありませんか?私はしょっちゅうの出来事です……。
そんな悩みを解決するため、M5Stackを使って今会議中だよ~って知らせるシステムを作ったので、作り方を紹介します。
この記事はこんな人におすすめ!
それでは始めましょう!
今回作ったものはこれ!会議中お知らせディスプレイ。
M5Stackは会議中の部屋の前もしくはリビングに置いておきます。
スマホから会議中ボタンを押すとM5Stackのディスプレイが赤くなるとともに”Meeting in Progress“と表示され、家族に会議中であることを知らせます。
これで家族が不用意に部屋に入ってくることはありません。会議が終わったらFreeボタンを押すとM5Stackのディスプレイが緑色に変わりますので、部屋に入ってもOKと家族に知らせることが出来ます。
スマホからM5Stackの操作は、WiFiルータを経由したUDP通信を使用しました。
システムを作るために必要なものは次のとおりです。
プログラムの流れは、M5Stack初期化→WiFi接続→UDP受信待ちとなります。M5Stackの表示モードは2つあり、スマホからの会議中またはFree信号によって切り替わるようにしました。
#include <M5Stack.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#define MEETING 1
#define FREE 0
void WiFiConnect();
const char* ssid = "*************";
const char* password = "*************";
WiFiUDP wifiUdp;
const char* smpIP = "***.***.***.***";
const int myPort = *****;
int displayMode; // 0: Free, 1: 会議中
void setup() {
displayMode = FREE;
M5.begin();
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);
WiFiConnect();
wifiUdp.begin(myPort);
}
void loop() {
switch (displayMode)
{
case FREE:
if(wifiUdp.parsePacket())
{
if((char)wifiUdp.read() == 'm'){
displayMode = MEETING;
M5.Lcd.setTextSize(5);
M5.Lcd.setCursor(0,20);
M5.Lcd.fillScreen(RED);
M5.Lcd.printf(" Meeting\n\n in\n\n Progress\n");
}
}
break;
case MEETING:
if(wifiUdp.parsePacket())
{
if((char)wifiUdp.read() == 'f'){
displayMode = FREE;
M5.Lcd.fillScreen(GREEN);
}
}
break;
default:
break;
}
}
void WiFiConnect(){
WiFi.begin(ssid,password);
while(WiFi.status() != WL_CONNECTED){
delay(500);
M5.Lcd.print('.');
}
M5.Lcd.print("\r\nWiFi connected\r\nIP address: ");
M5.Lcd.println(WiFi.localIP());
delay(2000);
M5.Lcd.fillScreen(GREEN);
}
WiFi接続方法、UDP通信方法の解説は、本記事で避けさせて頂きます。
詳細は▼の記事を参照下さい。
https://craft-gogo.com/m5stack-wifi/
https://craft-gogo.com/m5stack-udp/
スマホからM5StackへUDP通信をするためにWiFi TCP/UDP Controllerというアプリを使います。
▼のリンクからアプリをダウンロードしましょう。
ダウンロードしてWiFi TCP/UDP Controllerを起動したら、設定を変更していきます。
まずはNameから。
Nameは、ボタンの表示名です。
分かりやすい表示名にしましょう。今回はbutton1を’会議中‘、button2を’Free‘としました。
次にCommandです。ボタンを押したときに、どんな値を送信するか決めます。button1に’m‘、button2に’f‘を設定し、M5Stack側のプログラムに合わせます。
更にVisilibityの設定です。関係ないボタンは消してしまい、button1とbutton2だけ残しました。
これまでの設定が終わるとコントロール画面は▼になります。
各ボタンをクリックし、M5Stackのディスプレイが切り替われば完成です!
以上、M5Stackを使って今会議中だよ~って知らせるシステムの作り方紹介でした。
これでテレワーク中に家族が部屋に入ってきて、Webカメラに映りこんでしまった、音声が入りこんでしまったトラブルも解消出来ます。
M5StackとAndroidスマホがあれば直ぐに作れちゃいますので、興味のある方は是非挑戦してみて下さい。
]]>今回は、スマホからUDPでメッセージを送信して、M5Stackがこのメッセージを受信する実験をしたので記事にしてみました。
この記事はこんな人におすすめ!
▼ 完成形はこちら
スマホからUDP通信で1から9までを順にM5Stackへ送信した例です。M5Stack画面にスマホから送信したデータが表示されます。
以下では、この方法について解説していきます。
UDPとは、TCPと同じトランスポート層のプロトコルです。TCPとは異なり、相手にメッセージが届いたか管理を行わないコネクション型通信です。データ落ちが発生した場合の再送など行わないので、高速な分、信頼性は低いです。対象サービスやプロトコルを指定するために、ポート番号設定が必要です。
UDPメッセージを受信するための、サンプルプログラムです。
プログラムは、Wi-Fi接続→UDPメッセージ受信待ちといった流れです。
#include <M5Stack.h>
#include <WiFi.h>
#include <WiFiUdp.h>
void WiFiConnect();
const char* ssid = "**************";
const char* password = "*************";
WiFiUDP wifiUdp;
const int myPort = *****; // 1024~60253を設定
void setup() {
M5.begin();
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);
WiFiConnect();
wifiUdp.begin(myPort);
}
void loop() {
if(wifiUdp.parsePacket()){
char s;
s = (char)wifiUdp.read();
M5.Lcd.println(s);
}
}
void WiFiConnect(){
WiFi.begin(ssid,password);
while(WiFi.status() != WL_CONNECTED){
delay(500);
M5.Lcd.print('.');
}
M5.Lcd.print("\r\nWiFi connected\r\nIP address: ");
M5.Lcd.println(WiFi.localIP());
}
M5StackをWi-Fi接続するには、WiFiライブラリを使用します。begin関数の引数にWi-Fi機器のSSID、パスワードを設定し、begin関数を呼び出せば簡単にM5StackをWi-Fi接続出来ます。
M5StackのWi-Fi接続方法はこちらの記事で詳しく解説していますので、参照して下さい。
https://craft-gogo.com/m5stack-wifi/
UDPパケットを受信するには、WiFiUdpライブラリを使用します。
#include <WiFiUdp.h>
WiFiUDP wifiUdp;
const int myPort = *****; // 1024~60253を設定
void setup() {
wifiUdp.begin(myPort);
}
void loop() {
if(wifiUdp.parsePacket()){
char s;
s = (char)wifiUdp.read();
M5.Lcd.println(s);
}
}
まずbegin関数を呼び出すことで、引数で指定したポートを開放し、UDPメッセージを受信する準備をします。
parsePacket関数でパケットを受け取ったことを確認したら、read関数でデータを読み取り、ディスプレイに表示させるようにしました。
WiFi TCP/UDP Controllerというアプリを使えば、簡単にUDPパケットを送信することができます。
▼のリンクからアプリをダウンロードしましょう。
アプリを起動して、右上のスパナボタンをクリックし、UDP送信の設定をしましょう。
設定項目はたくさんありますが、以下の項目を設定しておけばOKです。
項目 | 設定 |
---|---|
IP or Domain Name | M5 StackのIPアドレス |
Port | M5Stackプログラムで指定したポート番号 |
TCP/UDP | UDP |
BUTTON Name | 自分が使いやすいようボタンのラベル名を設定 |
BUTTON Command | ボタンを押したときに送信するメッセージ |
以上、スマホからUDPでメッセージを送信して、M5Stackがこのメッセージを受信する方法の解説でした。
みなさんの参考になりましたら幸いです。
]]>クルマやバイクに乗っているとき、加速度が視覚化できたら、運転スキルを磨くのに活用できますよね。
3500円のM5StickC Plusを買って、100行以下の簡単なプログラムを書けば実現できますので、作り方を解説します。
プログラムのポイントは3つです。
それでは始めましょう!
製作物は下の写真になります。
M5StickC Plusのディスプレイに加速度を表示させました。
加速度が発生していないときは、赤い点はグラフ中心に位置し、加速度に応じて赤い点が上方向、減速度に応じて下方向に動きます。
カーブを曲がったときは、横加速度に応じて赤い点が横方向に移動します。
加速度がどれぐらい発生しているかは、円を基準にすれば分かります。一番外側の円を1G (=9.8m/s2)発生線としました。
M5StickCの取り付けは、裏面に両面テープなどで取り付けます。
垂直に取り付けられない場合に備えて、傾きを補正出来るようにしています。
M5StickCのAボタン(M5と書かれているボタン)を押すと傾きが補正され、赤い点がグラフ中央に位置するようになります。
全体のプログラムは以下のとおりです。
#include <M5StickCPlus.h>
#include <math.h>
TFT_eSprite sprite = TFT_eSprite(&M5.Lcd);
float OFFSET_X = 0, OFFSET_Y = 0, OFFSET_Z = -0.08;
float accX, accY, accZ, accU, accV, accW;
float x, y, z, theta;
int R1 = 20, R2 = 40, R3 = 60;
int X0 = 120, Y0 = 67;
void setup() {
M5.begin();
M5.IMU.Init();
M5.Lcd.setRotation(3);
// スプライトの作成
sprite.setRotation(3);
sprite.createSprite(M5.Lcd.width(), M5.Lcd.height());
}
void loop() {
sprite.fillRect(0, 0, M5.Lcd.width(), M5.Lcd.height(), BLACK);
// グラフ描画
sprite.drawCircle(X0, Y0, R1, WHITE);
sprite.drawCircle(X0, Y0, R2, WHITE);
sprite.drawCircle(X0, Y0, R3, WHITE);
sprite.drawLine(X0 - R3 - 10, Y0, X0 + R3 + 10, Y0, WHITE);
sprite.drawLine(X0, Y0 - R3 - 5, X0, Y0 + R3 + 5, WHITE);
// 加速度データ取得
M5.IMU.getAccelData(&x, &y, &z);
accZ = -(x + OFFSET_X);
accY = -(y + OFFSET_Y);
accX = -(z + OFFSET_Z);
accU = accX * sin(theta*PI/180) + accZ * cos(theta*PI/180);
accV = accY;
accW = - accX * cos(theta*PI/180) + accZ * sin(theta*PI/180);
sprite.setCursor(0,5,2);
sprite.printf("X:%5.2fG\nY:%5.2fG\nZ:%5.2fG", accU, accY, accW);
sprite.fillCircle(X0 + (int)(accV * R3), Y0 - (int)(accU * R3), 8, RED);
// 傾き補正
M5.update();
if(M5.BtnA.wasPressed()){
accZ = max(min(accZ, 1.0), -1.0);
theta = 180 / PI * asin(accZ);
}
// スプライトを表示画面に転送する
sprite.pushSprite(0, 0);
delay(100);
}
プログラムのポイントは3点です。
M5.IMU.Init();
M5.IMU.getAccelData(&x, &y, &z);
accZ = -(x + OFFSET_X);
accY = -(y + OFFSET_Y);
accX = -(z + OFFSET_Z);
加速度センサを初期化した後、getAccelData関数で加速度データを取得することが出来ます。
M5StickCの座標軸は写真のとおりです。
実際に取り付けたときに発生した加速度が分かりやすいよう、前後方向の加速度をaccX、横方向の加速度をaccY、垂直方向の加速度をaccZとし、取得した加速度データを座標変換しました。
私のM5StickCは、z軸の加速度値がオフセットしていましたので、補正をかけています。
M5StickCを垂直に設置しなくても前後方向の加速度を検出出来るよう、傾きを補正します。
静止した状態での傾斜角は以下式で求めることが出来ます。
$$accZ=g\sin\theta$$
$$\theta=\sin^{-1}(accZ/g)$$
M5StickのボタンA(M5と書かれているボタン)を押した時に傾斜角を求めるようにしました。
M5.update();
if(M5.BtnA.wasPressed()){
accZ = max(min(accZ, 1.0), -1.0);
theta = 180 / PI * asin(accZ);
}
M5StickCが傾いているとき、前後方向の加速度は以下式で求めることが出来ます。
$$accU = accX \sin\theta + accZ \cos\theta$$
accU = accX * sin(theta*PI/180) + accZ * cos(theta*PI/180);
取得した加速度データをリアルタイムでディスプレイに表示させますが、工夫しないとチラつきが発生してしまいます。
チラつき防止方法は、別記事で詳細に解説していますので、こちらを参照下さい。
https://craft-gogo.com/m5stack-sprite/
TFT_eSprite sprite = TFT_eSprite(&M5.Lcd);
sprite.createSprite(M5.Lcd.width(), M5.Lcd.height());
sprite.pushSprite(0, 0);
TFT_eSpriteクラスでSpriteを作成した後、Spriteに図形や文字を描画し、pushSprite関数でM5StickCのディスプレイにこのSpriteを表示させることでチラつきを防止します。
スプライトとは、コンピュータ上で動く図形を表現する際に、動かす図形と固定された背景とを別に作成し、ハードウェア上で合成することによって表示を高速化する手法のことである。
IT用語辞典バイナリ
以上、M5StickC Plusを使った加速度センサの作り方解説でした。
クルマやバイクにM5StickC Plusを取り付けることで、加速度を視覚化出来ます。
M5StickC Plusは3500円ほどで購入出来るので、気軽に製作トライできますよ。
プログラムのポイントは3つでした。
みなさんの参考になりましたら幸いです。
]]>M5Stackの画面に文字や図形を描画した際、画面がちらついて困っていませんか?
普通に画面描画するプログラムを組むとちらついてしまうんです。
本記事では、TFT_eSpriteクラスを使ったちらつき防止方法について、デジタル時計をプログラミングしながら解説します。
こんな人におすすめ!
それでは始めていきましょう。
そもそもSpriteって何?
スプライトとは、コンピュータ上で動く図形を表現する際に、動かす図形と固定された背景とを別に作成し、ハードウェア上で合成することによって表示を高速化する手法のことである。
IT用語辞典バイナリ
このようにSpriteを使うことによって画面ちらつきが防止出来るわけです。
TFT_eSpriteクラスは、TFT_eSPIというライブラリ内に含まれています。TFT_eSPIライブラリを使うにはM5Stackライブラリをインクルードするだけでよいです。
まず、TFT_eSpriteクラスをインスタンス化する必要があります。
TFT_eSprite sprite = TFT_eSprite(&M5.Lcd);
扱う色数を指定します。
sprite.setColorDepth(8);
引数は下表から選びます。
引数 | 定義 |
16 | 16ビット = 65536色 |
8 | 8ビット = 256色 |
1 | 1ビット = 2色 |
Spriteを作成します。
sprite.createSprite(M5.Lcd.width(), M5.Lcd.height());
第1引数にSpriteの横幅、第2引数にSpriteの高さを指定します。
Spriteに表示させたい図形や文字を描画していきます。
sprite.printf("文字描画の例");
M5Stackの画面に表示するときはM5.Lcd.printf(“xxx”)でしたが、M5.Lcdをspriteに変更する形となります。
spriteに描画した図形や文字をM5Stackの画面に表示させます。
sprite.pushSprite(0, 0);
第1引数は左端、第2引数は上端です。
#include <M5Stack.h>
#include <WiFi.h>
#define JST (3600L * 9)
const char* ssid = "***************";
const char* password = "**************";
TFT_eSprite sprite = TFT_eSprite(&M5.Lcd);
void WiFiConnect();
void setup() {
M5.begin();
M5.Lcd.setTextSize(2);
WiFiConnect();
M5.Lcd.setTextSize(3);
// NTPサーバと時刻を同期
configTime(JST, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");
// スプライトの作成
sprite.setColorDepth(8);
sprite.setTextSize(3);
sprite.createSprite(M5.Lcd.width(), M5.Lcd.height());
}
void loop() {
struct tm tm;
if(getLocalTime(&tm)){
sprite.fillScreen(BLACK);
sprite.setCursor(20,60);
sprite.setTextSize(6);
sprite.printf("%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
sprite.setCursor(40,140);
sprite.setTextSize(4);
sprite.printf("%d/%2d/%2d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
// スプライトを画面表示する
sprite.pushSprite(0, 0);
}
delay(1000);
}
void WiFiConnect(){
WiFi.begin(ssid,password);
while(WiFi.status() != WL_CONNECTED){
delay(500);
M5.Lcd.print('.');
}
M5.Lcd.print("\r\nWiFi connected\r\nIP address: ");
M5.Lcd.println(WiFi.localIP());
delay(500);
}
画面のちらつきが無くなったのが分かりますよね
TFT_eSpriteクラスを使ったちらつき防止方法について、デジタル時計をプログラミングしながら解説しました。
最後までお読み頂きありがとうございました。
私はスイッチサイエンスさん(Amazon)でM5Stackを買いましたので、M5Stackを持っていない方はどうぞ。
書籍もKindleストアで「みんなのM5 Stack入門」を購入し、読みながら勉強してまーす。
]]>「ホッチキスだけで取り付ける棚のように、M5StickCも壁を傷つけずに壁に固定できたらなぁ・・・」
自分で作ればいいじゃないかと思い、3Dプリンタで作ってみました。
今回の記事では、ホッチキスで固定できるM5StickC用壁掛けホルダーを3Dプリンタで製作した過程を紹介します。
製作の流れは、
といった流れです。
尚、3DデータはThingiverseというファイル共有サイトにアップロードしておきましたので、どなたでもダウンロード可能ですよ!
https://www.thingiverse.com/thing:5323021
それでは始めましょう。
FreeCADで3Dモデルを作成していきました。製作過程を紹介していきます。
3Dモデル出来上がり!
STLファイルをUltimaker Curaで読み込み、3Dプリントの設定をしていきます。
Standard Quality(0.25mm)、インフィル密度は30%に設定しました。
サポートは無しでも問題ないですよ。
私が持っている3Dプリンタ Creality Ender-3でプリント。プリント時間は17分。
https://craft-gogo.com/post-240/
こんな感じで出来上がりました!
ホッチキスで壁に固定です。
その上からM5StickCを4つの爪に引っかけると・・・
いい感じ!
ホッチキスで固定できるM5StickC用壁掛けホルダーを3Dプリンタで製作した過程を紹介しました。
この3DモデルはThingiverseにアップロードしていますので、製作に興味を持った方はダウンロードしてみて下さい。
https://www.thingiverse.com/thing:5323021
最後までお読み頂きありがとうございました。
]]>家庭菜園や夏休みの宿題などで毎日の気象データを自動で管理出来たら便利ですよね。
今回、M5Stickと気温、湿度、気圧センサが一体となったENVⅢを使って実現出来たので記事にします。自動管理は、LINE Notifyというサービスを使ってスマホへデータ送信する方法を取りました。
こんな人におすすめ!
製作方法は次の順で解説していきます。まず製作に必要な事前知識です。ENV Ⅲのデータ計測方法、LINE Notifyを使ったデータ送信方法、そしてディープスリープモードによる省電力を説明します。次に全体プログラムの解説になります。
今回用いる温度・湿度・気圧センサはENV HAT Ⅲというすべてセンサが1パッケージになっているものを使用します。
2022年3月時点の価格は1430円でした。
温度・湿度センサにはSHT30、気圧センサにはQMP6988というチップが使われています。通信方式はI2Cです。
スペックを載せておきますね。
項目 | パラメータ |
---|---|
温度測定範囲 | -40 ~ 120℃ |
最大測定精度 | 0 ~ 60℃ / ±0.2℃ |
湿度測定範囲 /誤差 | 10 ~ 90%RH / ±2% |
気圧測定範囲 /分解能 /誤差 | 300 ~ 1100 hPa / 0.06 Pa / ±3.9 Pa |
通信プロトコル | I2C:SHT30(0x44)、QMP6988(0x56) |
動作温度範囲 | 0 ~ 40℃ |
M5Stackからライブラリがリリースされているので、今回このライブラリを使用します。
私はArduino IDEではなく、Platform IO IDEを使用していますので、Librariesから「UNIT_ENV」を検索すればライブラリが見つかりますよ。
PlatformIO IDEの使い方については、こちらの記事で紹介していますので参考にして下さい。
https://craft-gogo.com/m5stack-build/
まずはUNIT_ENVライブラリを呼び出します。
#include "UNIT_ENV.h"
次にWireライブラリを初期化し、I2Cバスに接続します。
Wire.begin(0,26);
温度・湿度はSHT30、気圧はQMP6988を使用していますので、まずはSHT30の解説から。
SHT3Xクラスをインスタンス化し、
SHT3X sht30;
get()関数を呼び出すと温度、湿度が計測されます。
sht30.get()
温度はcTemp、湿度はhumidityに格納されています。
float temp = sht30.cTemp;
float hum = sht30.humidity;
続いてQMP6988の解説。同じようにQMP6988クラスをインスタンス化、初期化し、
QMP6988 qmp6988;
qmp6988.init();
calcPressure()関数を呼び出せば戻り値として気圧が返ってきます。
float pressure = qmp6988.calcPressure();
ライブラリを使えば簡単ですね!
3時間おきにLINE Notifyで温度・湿度・気圧データをLINEへ送信します。
LINE Notifyの使い方はこちらの記事で紹介していますので、今回割愛させていただきます。
https://craft-gogo.com/m5stack-line/
データ計測は屋外を想定していますので、なるべくM5Stickを省電力化し、バッテリーを長持ちさせたいです。ここでは3つの方法で省電力化していきます。ディープスリープモード、クロック周波数変更、液晶バックライト変更です。
データ計測は頻繁に行わないため、待機中の電力消費はもったいないです。
M5Stickに搭載されているマイコンESP32はディープスリープモードという省電力モードがありますので、これを使用しました。
esp_deep_sleep(10800000000ULL); // 3 * 60 * 60 * 1000000 μs = 3h
引数で指定した時間スリープした後、マイコンがリセットされます。引数はμsec単位で指定します。
ディープスリープモードはM5Stick自体の温度を下げる目的もあります。連続でデータ計測していると計測温度はどんどん上がっていくので、正確な温度測定にはディープスリープモードにして温度を下げる必要があります。
CPUのクロック周波数を下げることも省電力化には有効です。
setCpuFrequencyMhz(80);
引数の単位はMHzです。デフォルトは240MHzで動作しています。今回、80MHzまで下げました。下げすぎると無線通信が安定しなくなるようです。
液晶を暗くすることも省電力化に繋がりますよね。
M5.Axp.ScreenBreath(0);
引数は明るさで0-12の範囲で指定します。液晶は見ない前提なのでゼロまで下げました。
プログラム全体を載せます。
#include <M5StickCPlus.h>
#include "UNIT_ENV.h"
#include <WiFiClientSecure.h>
void Line_notify(String msg);
SHT3X sht30;
QMP6988 qmp6988;
WiFiClientSecure client;
const char* ssid = "*************";
const char* password = "*****************";
const char* host = "notify-api.line.me";
const char* token = "************************";
float tmp = 0.0;
float hum = 0.0;
float pressure = 0.0;
double vbat = 0.0;
void setup() {
M5.begin();
setCpuFrequencyMhz(80);
M5.Axp.ScreenBreath(0);
M5.Lcd.setRotation(3);
M5.Lcd.setCursor(0,0,4);
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED){
M5.Lcd.print('.');
delay(500);
}
M5.Lcd.print("\r\nWiFi connected\r\nIP address: ");
M5.Lcd.println(WiFi.localIP());
Wire.begin(0,26);
qmp6988.init();
pressure = qmp6988.calcPressure();
if(sht30.get()==0){
tmp = sht30.cTemp;
hum = sht30.humidity;
}else{
tmp=0,hum=0;
}
vbat = M5.Axp.GetVbatData() * 1.1 / 1000;
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0,0,4);
M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f \r\nPressure: %2.0fPa\r\nVolt: %.2fV \r\n", tmp, hum, pressure, vbat);
char msg[100];
sprintf(msg, "Temp: %2.1f \r\nHumi: %2.0f \r\nPressure: %2.0fPa\r\nVolt: %.2fV \r\n", tmp, hum, pressure, vbat);
Line_notify(msg); // LINE Notifyへリクエスト送信
esp_deep_sleep(10800000000ULL); // 3 * 60 * 60 * 1000000 μs = 3h
}
void loop() {
}
void Line_notify(String msg) {
client.setInsecure();
if (!client.connect(host, 443)) {
delay(2000);
M5.Lcd.print("Not Connected");
return;
}
String query = String("message=") + msg;
String request = String("") +
"POST /api/notify HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Authorization: Bearer " + token + "\r\n" +
"Content-Length: " + String(query.length()) + "\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n\r\n" +
query + "\r\n";
client.print(request);
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
break;
}
}
delay(2000);
}
プログラムを書き込むとさっそくLINEで温度・湿度・気圧データが送られてきます。
ほったらかしにして待っていると、数分の誤差はあるものの3時間毎に温度・湿度・気温データがLINEで送られてきました。
電源電圧の下がり方は3時間で0.1Vほどです。電源電圧4.17Vから始めたのですが12時間はバッテリーが持ちました。
M5StickとENVⅢを使って3時間ごとに温度・湿度・気圧データをLINEでスマホへ送信するシステム構築の解説をしました。
家庭菜園や夏休みの宿題などで毎日の気象データを自動で管理出来ますね。
今回はM5StickC PLUSのバッテリー駆動のみで12時間しか持ちませんでした。屋外で使用するとなった場合、電源確保必要ですので、今後の課題にしたいと思います。
興味を持たれた方は是非実践してみて下さい。
最後までお読み頂きありがとうございました。
私はM5StickC PLUSとENV ⅢをAmazon(スイッチサイエンス)で購入しました。みんなのM5Stack入門を読んで勉強してます。
]]>