概要
前回は画面に特化しましたが、今回はM5StickC Plus2が搭載している機能の呼び出し方メインにまとめてみました。
LED
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
M5.Display.setTextSize(4);
}
void loop(void) {
static uint8_t led = 0;
M5.delay(1);
M5.update();
if (M5.BtnA.isPressed()) {
led++;
M5.Display.setCursor(0, 0);
M5.Log.printf("%3d\n", led);
M5.Power.setLed(led); // 0:OFF 1-255:明るさ指定
M5.delay(10);
}
}
こちらはM5.Power.setLed()で明るさ指定をするだけです。ただしIRも同時に点灯してしまうので注意してください。点灯しっぱなしにすると本体が思ったより熱くなったので注意してください。
ただLEDとIRを共用している関係でONにすると150mA程度電流が流れます。IR用の短時間での点滅であれば問題ありませんが、ゆっくりした間隔でのLチカを行うとバッテリーやIR素子へのダメージがあるかもしれません。
IR送信
#include <IRsend.h> // IRremoteESP8266
#include <M5Unified.h>
IRsend *irsend;
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
// IRピン設定
uint8_t irPin = GPIO_NUM_MAX;
bool irInverted = false;
switch (M5.getBoard()) {
case m5::board_t::board_M5StickCPlus:
irPin = GPIO_NUM_9;
irInverted = true; // PlusはLowで点灯するので反転
break;
case m5::board_t::board_M5StickCPlus2:
irPin = GPIO_NUM_19;
break;
}
M5.Log.printf("IR irPin = %d, irInverted = %d\n", irPin, irInverted);
if (irPin != GPIO_NUM_MAX) {
irsend = new IRsend(irPin, irInverted);
}
}
void loop(void) {
M5.delay(1);
M5.update();
if (M5.BtnA.wasPressed()) {
// 送信
if (irsend) {
uint64_t send = irsend->encodeNEC(0x0000, 22);
irsend->sendNEC(send);
M5.Log.println("IR Send");
}
}
}
M5UnifiedはIR送信をサポートしていませんので、別ライブラリのIRremoteESP8266を利用して送信するのが一般的だと思います。
RTCセット
#define WIFI_SSID "WIFI_SSID"
#define WIFI_PASSWORD "WIFI_PASSWORD"
#define NTP_TIMEZONE "JST-9" // https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h
#define NTP_SERVER1 "0.pool.ntp.org"
#define NTP_SERVER2 "1.pool.ntp.org"
#define NTP_SERVER3 "2.pool.ntp.org"
#include <WiFi.h>
#include <sntp.h>
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
// 非同期でNTPと時間合わせをする
configTzTime(NTP_TIMEZONE, NTP_SERVER1, NTP_SERVER2, NTP_SERVER3);
// WiFiに接続する
M5.Log.print("WiFi:");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
// WiFi接続待ち
for (int i = 20; i && WiFi.status() != WL_CONNECTED; --i) {
M5.Log.print(".");
M5.delay(500);
}
M5.Log.println();
if (WiFi.status() == WL_CONNECTED) {
// WiFi接続完了
M5.Log.println("WiFi Connected.");
M5.Log.print("NTP:");
while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) {
// NTP同期待ち
M5.Log.print(".");
delay(1000);
}
M5.Log.println("NTP Connected.");
// ぴったり秒開始時点まで待機
time_t t = time(nullptr) + 1; // 1秒後を設定
while (t > time(nullptr)) {
// 時間待機
}
// RTCに時間を設定する
M5.Rtc.setDateTime(localtime(&t));
} else {
// WiFi接続失敗
M5.Log.println("WiFi none...");
}
M5.Display.clear();
}
void loop(void) {
static constexpr const char* const wd[7] = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };
delay(500);
// RTC時刻取得
m5::rtc_datetime_t dt = M5.Rtc.getDateTime();
M5.Display.setCursor(0, 0);
M5.Log.printf("RTC :%04d/%02d/%02d (%s) %02d:%02d:%02d\r\n",
dt.date.year, dt.date.month, dt.date.date, wd[dt.date.weekDay],
dt.time.hours, dt.time.minutes, dt.time.seconds);
}
Arduino 1.0.6以降はNTP同期を定期的に実行するSNTPが利用できるようになっています。
タイムゾーンは上記などを参考にして設定してください。日本は「JST-9」で中国は「CST-8」などになります。また、RTCに保存する時刻にはタイムゾーンの設定ができないので、タイムゾーン無しのローカルタイムを設定しています。
RTC呼び出し
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
}
void loop(void) {
static constexpr const char* const wd[7] = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };
delay(500);
// RTC時刻取得
m5::rtc_datetime_t dt = M5.Rtc.getDateTime();
M5.Display.setCursor(0, 0);
M5.Log.printf("RTC :%04d/%02d/%02d (%s) %02d:%02d:%02d\n",
dt.date.year, dt.date.month, dt.date.date, wd[dt.date.weekDay],
dt.time.hours, dt.time.minutes, dt.time.seconds);
// 起動時にESP32本体にRTCの時刻が自動設定されている
auto t = time(nullptr);
auto tm = localtime(&t);
M5.Log.printf("ESP32:%04d/%02d/%02d (%s) %02d:%02d:%02d\r\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, wd[tm->tm_wday],
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
起動時に自動的にRTCの時刻をESP32本体に設定してくれています。そのためRTCから読み出すよりは本体のローカルタイムを取得する方法でもよいと思います。
ただし、NTP取得時に設定したタイムゾーンは反映されていませんので、内部的にはGMT0のタイムゾーン設定無しですが、設定されている時刻は日本などのローカル時間になります。
通常はこの設定で問題ないと思いますが、時差を厳密に処理したい場合にはNTPへのタイムゾーンをやめて、すべてGMT0で処理した方がよいと思います。
IMU
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
}
void loop(void) {
M5.delay(1);
M5.update();
auto imu_update = M5.Imu.update();
if (imu_update) {
m5::IMU_Class::imu_data_t data = M5.Imu.getImuData();
M5.Display.setCursor(0, 0);
M5.Log.printf("accel x = %10.5f\n", data.accel.x);
M5.Log.printf("accel y = %10.5f\n", data.accel.y);
M5.Log.printf("accel z = %10.5f\n", data.accel.z);
M5.Log.printf("gyro x = %10.5f\n", data.gyro.x);
M5.Log.printf("gyro y = %10.5f\n", data.gyro.y);
M5.Log.printf("gyro z = %10.5f\n", data.gyro.z);
M5.Log.printf("mag x = %10.5f\n", data.mag.x); // Plus2では取得できない
M5.Log.printf("mag y = %10.5f\n", data.mag.y); // Plus2では取得できない
M5.Log.printf("mag z = %10.5f\n", data.mag.z); // Plus2では取得できない
}
}
従来どおりに取得可能です。搭載されているIMUは6軸ですので、方位の取得はできません。
ボタン
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
M5.Display.setTextScroll(true);
}
void loop(void) {
M5.delay(1);
M5.update();
if(M5.BtnA.wasPressed()){
// 真ん中のボタンを押して離した
M5.Log.println("BtnA wasPressed");
}
if(M5.BtnB.wasPressed()){
// 右側のボタンを押して離した
M5.Log.println("BtnB wasPressed");
}
if(M5.BtnPWR.wasPressed()){
// 左側のボタンを押して離した
// このボタンは6秒以上押すと電源オフになるので注意する
M5.Log.println("BtnPWR wasPressed");
}
}
こちらも従来と変わりません。左側のボタンは電源と共通ですので6秒以上押すと電源オフになるので注意してください。また、Plusだとショートとロングプレスしか判定できませんでしたが、Plus2の場合には通常のボタンと同じように利用可能です。ただし、非常に押しにくいので多用するのはおすすめしません。
ブザー
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
M5.Display.setTextScroll(true);
}
void loop(void) {
M5.delay(1);
M5.update();
if (M5.BtnA.wasPressed()) {
// 真ん中のボタンを押して離した
M5.Speaker.tone(2000, 1000); // 2000Hzを1000ms(1秒)
M5.Log.println("BtnA TONE(2000Hz)");
}
if (M5.BtnB.wasPressed()) {
// 右側のボタンを押して離した
M5.Speaker.tone(4000, 1000); // 4000Hzを1000ms(1秒)
M5.Log.println("BtnB TONE(4000Hz)");
}
if (M5.BtnPWR.wasPressed()) {
// 左側のボタンを押して離した
for (int i = 0; i < 4; i++) {
int frequency = 523.251 * pow(2, i);
M5.Speaker.tone(frequency, 500);
M5.Log.printf("BtnPWR TONE(C%d %dHz)\n", 5 + i, frequency);
M5.delay(500);
frequency = 587.330 * pow(2, i);
M5.Speaker.tone(frequency, 500);
M5.Log.printf("BtnPWR TONE(D%d %dHz)\n", 5 + i, frequency);
M5.delay(500);
frequency = 659.255 * pow(2, i);
M5.Speaker.tone(frequency, 500);
M5.Log.printf("BtnPWR TONE(E%d %dHz)\n", 5 + i, frequency);
M5.delay(500);
M5.delay(500);
}
}
}
M5.Speaker.tone()関数で音を出すことが可能です。しかしながらPlus2のブザーではあまり音質はよくありません。内部的にはきれいなサイン波をミキサー処理により出力している処理になっています。複数音を同時に再生することができるのですが、低い周波数はあまりきれいに聞こえません。
ブザー(Arduino標準tone関数)
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
M5.Display.setTextScroll(true);
}
void loop(void) {
M5.delay(1);
M5.update();
if (M5.BtnA.wasPressed()) {
// 真ん中のボタンを押して離した
tone(GPIO_NUM_2, 2000, 1000); // 2000Hzを1000ms(1秒)
M5.Log.println("BtnA TONE(2000Hz)");
}
if (M5.BtnB.wasPressed()) {
// 右側のボタンを押して離した
tone(GPIO_NUM_2, 4000, 1000); // 4000Hzを1000ms(1秒)
M5.Log.println("BtnB TONE(4000Hz)");
}
if (M5.BtnPWR.wasPressed()) {
// 左側のボタンを押して離した
for (int i = 0; i < 4; i++) {
int frequency = 523.251 * pow(2, i);
tone(GPIO_NUM_2, frequency, 500);
M5.Log.printf("BtnPWR TONE(C%d %dHz)\n", 5 + i, frequency);
frequency = 587.330 * pow(2, i);
tone(GPIO_NUM_2, frequency, 500);
M5.Log.printf("BtnPWR TONE(D%d %dHz)\n", 5 + i, frequency);
frequency = 659.255 * pow(2, i);
tone(GPIO_NUM_2, frequency, 500);
M5.Log.printf("BtnPWR TONE(E%d %dHz)\n", 5 + i, frequency);
// 休符は周波数0で指定
tone(GPIO_NUM_2, 0, 500);
}
}
}
Arduino標準のtone()関数を利用したサンプルです。単音しか鳴らすことができませんが、ブザーの場合にはこちらの方が音質がよいです。また、128個までの音を登録でき、順番に再生してくれます。複数音を登録すれば、曲などもかんたんに再生できるのですが現在途中で止めることができませんので注意してください。
マイク
#include <M5Unified.h>
static constexpr const size_t record_samplerate = 16000; // 録音サンプリングレート
static constexpr const size_t record_length = 240; // 録音数
static int16_t *rec_data; // 録音データ
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
// 画面を横にする
M5.Display.setRotation(1);
// 録音用メモリ確保
rec_data = (typeof(rec_data))heap_caps_malloc(record_length * sizeof(int16_t), MALLOC_CAP_8BIT);
memset(rec_data, 0, record_length * sizeof(int16_t));
// マイク開始
M5.Mic.begin();
}
void loop(void) {
M5.delay(1);
M5.update();
if (M5.Mic.isEnabled()) {
int16_t max = 0;
// 描画開始
M5.Display.startWrite();
// 録音できているか確認
if (M5.Mic.record(rec_data, record_length, record_samplerate)) {
// 画面横幅より長いデータは横幅までに制限
int32_t w = M5.Display.width();
if (w > record_length - 1) {
w = record_length - 1;
}
// 画面上に描画
for (int32_t x = 0; x < w; ++x) {
static int16_t prev_y[record_length]; // 前回座標を保存
static int16_t prev_h[record_length]; // 前回座標を保存
// 前回の線を消す
M5.Display.writeFastVLine(x, prev_y[x], prev_h[x], TFT_BLACK);
// 音の大きさをshiftで小さくして、画面上の高さに変換
static constexpr int shift = 6;
int32_t y1 = (rec_data[x] >> shift);
int32_t y2 = (rec_data[x + 1] >> shift);
if (y1 > y2) {
int32_t tmp = y1;
y1 = y2;
y2 = tmp;
}
// 画面中央からの相対座標に変換
int32_t y = (M5.Display.height() >> 1) + y1;
int32_t h = (M5.Display.height() >> 1) + y2 + 1 - y;
prev_y[x] = y;
prev_h[x] = h;
// 描画
M5.Display.writeFastVLine(x, y, h, TFT_WHITE);
// 最大音量確認
if (max < abs(rec_data[x])) {
max = abs(rec_data[x]);
}
}
}
// 最大音量表示
M5.Display.setCursor(0, 0);
M5.Log.printf("MAX = %5d\n", max);
// 描画終わり
M5.Display.endWrite();
}
}
環境音センサーとして利用した例になります。一定以上大きな音を検知するなどの場合には比較的かんたんに利用可能です。

シリアルプロッタなどで確認をすると、音の大きさに応じて数値が取れているのがわかると思います。
バッテリー残量取得
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
M5.Display.setTextScroll(true);
// 画面を横にする
M5.Display.setRotation(1);
// 消費電力アップのためにLEDを付ける
M5.Power.setLed(255);
}
void loop(void) {
M5.delay(1000);
M5.update();
M5.Log.printf("Battery Level = %3d %%, Voltage = %4d mV\n", M5.Power.getBatteryLevel(), M5.Power.getBatteryVoltage());
}
AXP192がなくなったことで、充電中の取得が取れなくなっています。バッテリー残量は従来どおり取得が可能でした。
電源オフ
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.clear(TFT_RED);
}
void loop(void) {
M5.delay(1);
M5.update();
if (M5.BtnA.wasPressed()) {
M5.Power.powerOff();
}
}
電源オフも従来と同じ方法です。ただし、USB接続中には電源が切れなくなっているか、おなしな状態になります。そして、USB接続中かを判定することができないので非常に面倒です。
秒タイマー起動
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.clear(TFT_RED);
}
void loop(void) {
M5.delay(1);
M5.update();
if (M5.BtnA.wasPressed()) {
// 5秒間スリープ
M5.Power.timerSleep(5);
// ここは実行されずにリセットされて起動する
M5.Display.clear(TFT_BLUE);
}
}
秒数指定して電源オフにしてスリープするモードです。こちらもUSB接続中は電源オフにならないので、擬似的にESP32側のスリープをしている場合があります。
時間指定起動
#include <M5Unified.h>
void setup(void) {
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.clear(TFT_RED);
M5.delay(500); // Serial出力用のウエイト
// 画面上にログを表示する
M5.setLogDisplayIndex(0);
}
void loop(void) {
M5.delay(1);
M5.update();
m5::rtc_datetime_t dt = M5.Rtc.getDateTime();
M5.Display.setCursor(0, 0);
M5.Log.printf("%04d/%02d/%02d %02d:%02d:%02d\n",
dt.date.year, dt.date.month, dt.date.date,
dt.time.hours, dt.time.minutes, dt.time.seconds);
if (M5.BtnA.wasPressed()) {
m5::rtc_time_t time;
// 時間指定なし
time.hours = -1; // -1だと時間指定無し
// 1分後をセット(毎時0分に起動の場合には0を指定)
time.minutes = (dt.time.minutes + 1) % 60;
// 現在時刻と起動時間表示
M5.Log.printf("NOW %02d:%02d:%02d\n", dt.time.hours, dt.time.minutes, dt.time.seconds);
M5.Log.printf("TIMER %02d:%02d:00\n", time.hours, time.minutes);
// USB接続中の場合にはタイマーが動作せずに、電源ボタンで復帰する
esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, false);
// 指定時間までスリープ
M5.Power.timerSleep(time);
// ここは実行されずにリセットされて起動する
M5.Display.clear(TFT_BLUE);
}
}
RTCの時刻設定で起動することも可能です。ただし、毎時0分に起動などはかんたんなのですが、5分間隔に正確に起動する場合などはちょっと指定が面倒です。
USB接続時には擬似的にスリープしますが、RTCの通知を受け取れないために復帰することができません。基本的に非常に使いにくいモードのために、Plus2では使わないほうがよいと思います。
まとめ
いろいろ機能を確認してみましたが、USB接続中には電源オフにならない仕様のため、RTC通知まわりがかなりわかりにくいです。PlusだとRTCの通知がGPIOで取得可能だったため、スリープからの復帰などに利用可能でしたがPlus2だと完全にアクセスできない場所に分離されています。
RTCの通知か、USB給電中かを判定できればもう少しきれいな動作になりそうだったのですが残念です。ただUSB給電中かは継続的にバッテリー電圧を監視することで、擬似的には判定可能なのでもう少し研究してみたいと思います。



コメント
こんにちは。tone()を使う「Arduino標準のtone()関数を利用したサンプル」を試してみましたがtone()が定義されていないというコンパイルエラーになります。#include を追加しても駄目でした。何か足りないのでしょうか?
Arduino IDEを使っているのであればESP32のボードマネージャのバージョンが古いのではないでしょうか?
標準のesp32 by Espressifだと2.0.14が最新です
M5Stack版だと2.1.1が最新になります
どちらも2.0.3以降であれば利用可能なはずです
ボードマネージャーからバージョンアップできないか試してみてください
なんとその通りでした! Updateして動きました。はいarduino環境でした。ありがとうございました。いまPLUS2に移植作業をしていてブザー音が良く聞こえなくなってしまい困っていたのですが助かりました。ありがとうございます。
たびたびすみません質問した者です。新たな問題が発生しました。esp32 by Espressifをボードマネージャで出て来る最新の2.0.11にしたらtone()が使えるようになったのですが、M5StickC PLUS2が特定の2つほどのルーチン(1つのプログラム内)で異常に遅くなりました。遅すぎてキー入力がうまく拾えないです。tone()は無関係のようです(書き換えて使わなくしても遅いままです)。そしてesp32 by Espressifを1.0.6に戻したら元通り速くなりました。しかしtone()は使えません。遅い原因はわかりません。何らかの命令によって遅くなるような気がしますが特定できていません。2.0.14はボードマネージャで出てこないようです。
んー、なかなか難しいですね
上記のPWMを自分で使う方法であれば古いバージョンで音がなれせると思います
#define SPEAKER_PIN GPIO_NUM_2
#define SPEAKER_CH 1
void setup()
{
// 初期化
pinMode(SPEAKER_PIN, OUTPUT);
ledcSetup(SPEAKER_CH, 12000, 8);
ledcAttachPin(SPEAKER_PIN, SPEAKER_CH);
}
void loop()
{
// 1200Hzの周波数を出力
ledcWriteTone(SPEAKER_CH, 1200);
// 0.5秒待機
delay(500);
// 音を止める
ledcWriteTone(SPEAKER_CH, 0);
// 音の間隔を1秒あける
delay(1000);
}
ありがとうございます、いろいろやってみます。esp32 by Espressifを2.0.14にしても駄目でした。そしてライブラリ周りを色々やっていたら、1.0.6に戻しても遅いままになってしまいました。ちょっとはまっています。すべての動作が遅いわけではないのでよくわかりません。ライブラリの仕様変更で特定の命令が遅くなったのかもしれません。何かわかりましたらお知らせします。
追伸:少しわかりました。M5.BtnA.wasPressed() が全く機能しないようです。コンパイルエラーは出ません。M5.BtnA.isPressed()は機能します。しかしキーバッファは見ないので、速いループでM5.BtnA.isPressed()を頻繁に呼び出していないと拾えません。また、取りこぼしが出ます。ヘッダは でもでも同じでした(どちらか1つ)。シンプルなコードを作ってテストしました。
詳細がわかりました。M5.update()を2回連続して実行すると、その後のM5.BtnA.wasPressed()が全く反応しなくなります。ずっと押していても拾わなくなります。M5.BtnA.isPressed()は機能します(バッファリングはしません)。サブルーチンに飛んで再びM5.update()を実行する場合があるので拾えなかったようです。M5.update()の2回連続、を避けるとwasPressed()はisPressed()と同じくバッファリングなしでなら動作します。よってキーバッファを利用したセンスができなくなりました。
M5.update()の中でボタンの状態取得をしています
そのときに前回からの差分を判定してwasPressed系の取得をしていますので、2回連続実行すると差分がなくなって常にfalseになってしまいます
M5.update()はloopの先頭で1度のみ呼び出すことをおすすめします
たしかM5Unified.hではなくて、古いライブラリの場合にはM5.BtnA.wasPressed()を呼び出すとフラグがfalseになる仕様でしたが、M5Unified.hだとM5.update()で処理していますので若干動きが異なるはずです
ledcWriteTone()でも音が鳴りました、ありがとうございました。
M5StickC plus2で倒立振子を作成しています。
ロール角を取得しようとしたところ、#include では、
以下のコマンドがしようできないようです。
M5.IMU.getAhrsData(&pitch,&roll,&yaw);
解決方法やアドバイスがあればいただけると幸いです。
Plus2のライブラリにはgetAhrsDataが未実装でした
中身をみたところライブラリをよのまま呼び出しているだけでしたので、同等の処理を自分で呼び出すのがよさそうです
void getAhrsData(float* pitch, float* roll, float* yaw) {
float accX = 0;
float accY = 0;
float accZ = 0;
float gyroX = 0;
float gyroY = 0;
float gyroZ = 0;
M5.IMU.getGyroData(&gyroX, &gyroY, &gyroZ);
M5.IMU.getAccelData(&accX, &accY, &accZ);
MahonyAHRSupdateIMU(gyroX * DEG_TO_RAD, gyroY * DEG_TO_RAD,
gyroZ * DEG_TO_RAD, accX, accY, accZ, pitch, roll, yaw);
}
https://github.com/m5stack/M5StickC-Plus/blob/master/src/utility/MPU6886.cpp#L215C1-L229C2
上記の部分になります
https://github.com/m5stack/M5StickC-Plus/tree/master/src/utility
あとは上記にある
MahonyAHRS.cpp
MahonyAHRS.h
の2つのファイルをコピーしてきてプロジェクトの中にいれる必要があります
こんばんは。
2024年7月から M5StickC-Plus2を使い始めました。
こちらのサイトは有益な情報が多く、大変役立っており、感謝申し上げます。
さて、現在、ArduinoIDEやPlatFormIOで学習中ですが、どうしても解決できない
不具合があり困っています。
それは LovyanGFX というグラフィクスライブラリに関するものです。
LovyanGFXに付属するサンプルプログラムは動作するのですが、USBケーブルを
外すと電源が切れてしまうのです。Powerスイッチを押していると動作しますが、
押し続けると電源は切れてしまいます。
十個程度のサンプルプログラムを試しましたが、どれも同じでした。
M5GFXやM5Unifiedなどの類似のライブラリにはこうした不具合はありませんが、
LovyanGFXはそれらにはない利点があり、何とか改善したいと思っています。
私の使い方によるものか、何か工夫が必要なのかをご存知であれば教えて下さい。
よろしくお願いいたします。
LovyanGFXとM5GFXは中身がほぼ同じです
作者が同じで、M5Stackに関連しているものだけ抜き出したのがM5GFXになります
M5UnifiedはM5GFXを内部に利用しています
https://docs.m5stack.com/ja/core/M5StickC%20PLUS2
電源まわりですが、Plus2の場合には「電源を維持するためにプログラムの初期化でhold(G4)ピンをハイレベル(1)に設定する必要があります。」とありますので、自分で処理を追加する必要があります
M5Unifiedなどを使うと自動的に内部で初期化処理をしてくれていると思います
// HOLD ON
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
自分で実行する場合には上記の処理になると思います。
上記の記事では自分でHOLDしています。
たなかまさゆき さん、早速のアドバイスに感謝申し上げます。
// HOLD ON 以下を setup()に追加したところ、私の報告した
不具合は解決しました。
付属の説明書は読んでいたつもりでしたが、このような注意点が
あったとは知りませんでした。
重ねて感謝申し上げます。
うまく動いて良かったです!
いつもお世話になっております。
plus2買って使い始めて参考にさせて頂いてます。
緑のLEDについては特に紹介はされてない…?ようですが
あれは制御で使えるものではないでしょうか?
https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/512/Sch_M5StickC_Plus2_sch_02.png
緑LEDは上記の左下にあるのですが、基本は制御できなかった気がします
5V入力があって、ESP32の電源が切れているときに緑が付く?
ありがとうございます。
電源切れてて
充電されて、というか通電されてたら付くだけ…なんですね。
やっぱしそうなんですね。。。
でもって、通電した状態でOFFにすると、
通電したままで再度ONにできないことないですか?
あれって仕様…なんでしょか。。。
使いにくいだけのような気がするけど。
後、電源も数秒ボタン押しておかないとONにできない
感じですよね。
たしかに謎なLEDですよね
ESP32-S3系だと押しっぱなしでダウンロードモードに入るので、その確認用ですがESP32だとダウンロードモードでもないですし
6秒で電源OFFになる確認用のLED?
電源ONも2秒押しっぱなしなのでちょっと不便です
電源ON、自分のは3秒は押さないといけない…感じです
2秒、どこかに仕様で書かれてるんです?
USBを繋いだ状態でOFFにすると、
USB繋いだままでは再度ONにできない件については…
M5Stackに聞いたら、、、以下の回答が来ました… ^^;
It is a hw bug, we will add this testing part for all the future products, and we had not found this before, we will think about the solution
https://www.switch-science.com/products/9350
ボタンC(G35)が追加
2秒押しで電源ON、6秒押しで電源OFF
公式では2秒、6秒なんですけれどキャパシタと抵抗での時定数制御で、誤差で微妙に秒数ズレている気がするんですよね
OFFも手元のは5秒ぐらいな気がする
https://docs.m5stack.com/en/core/M5StickC%20PLUS2
公式ドキュメントだと
power it off (hold the power button until the green light turns on, indicating it is powered off)
緑はパワーオフ用って書いてあるのかな?
電源オン:
「ボタンC」ボタンを2秒以上押すか、RTCタイマーによってトリガーされたIRQ信号を介してウェイクアップします。ウェイクアップ信号をトリガーした後、プログラムの初期化でホールド(G4)ピンをハイ(1)に設定して電源を維持する必要があります。そうしないと、デバイスは再び電源オフ状態になります。
電源オフ:
外部USB電源がない場合は、「ボタンC」ボタンを6秒以上押します。または、外部USB電源がない場合は、プログラムでHOLD(GPIO4)= 0に設定して電源をオフにします。USB接続時に「ボタンC」ボタンを6秒以上押すと、画面がオフになり、スリープモードになりますが、電源はオフになりません。
そうだ、USB接続していると電源OFFしているようにみえるけれど、OFFではないからONに戻らないんだ
>USB接続していると電源OFFしているようにみえるけれど、
>OFFではないからONに戻らないんだ
やっぱし、そーいうことなんですね…
ユーザーには見た目にはOFFになったようにしか見えないから、、、
結局、一回USB抜いて、挿し直して電源ONにする…って
感じの使い方するしかないくなっていしまう
使いにくいですねー
ファームウェアの修正とかでなんとかなりそうな気がするけど
できないのかな…
CoreInk系がこの仕様なんですよね
ハードウエア的な回路で省電力に特化しているのでUSB給電中は微妙な動作になっています
なるほど・・・
ちょっと使いにくくなった感 ありますね…
電源管理 TIMER POWER ってありますが何なんでしょ?
RTCのタイマー割り込みを使って電源オンにできます
M5StickCにもRTCのタイマー割り込みが使えたのですが、電源管理のAXP192がいたのでESP32だけ電源落としてもぜんぜん省電力にならなかった感じです
Plus2だと電源管理のICがなくなって、主電源を切る感じの回路に変更されています
RTCは独自のバックアップバッテリーがあるので主電源OFFでも動いて割り込みができます
その割り込みで主電源をONにする機能がついています
>ハードウエア的な回路で省電力に特化しているのでUSB給電中は微妙な動作になっています
なるほど…
給電中でなくても電源系、負荷耐量が低いような気がするのですが
その辺り何か調べられたりしてますでしょうか。
plusでは5V出力をサーボ電源への利用してもなんともなかったのですが
plus2では負荷が大きくなっていくと落ちます…
正確な状態はわかりません。電源ONボタンを押しても電源は入らなくなります。
USBを挿すと電源が入り復帰できます。
昔はAXP192があったので、5Vと3.3V系がある程度分離して動いていたのですがなくなったので消費電力は減ったのですが共倒れするようになったみたいですね
AXP192が製造中止になった影響での仕様変更なのでしかたないのですが、M5StickCはそんなに省電力よりじゃないほうが嬉しかったです
ありがとうございます。
旧型のplusではAXP192の過電流保護が働いて出力電流を絞ってくれていたのが
plus2ではなくなったので過電流まで行っちゃって共倒れに至り落ちる…ということですね。
使う側で過電流にならないよう気を付けて使わないといけなくなったわけか…
M5の使い勝手の良さが悪くなった感で残念ですね…
M5StickC系はバッテリーから直でモーター制御とか無茶も結構いけたのが便利でしたよね
Plus2はちょっと毛色が違うので残念です
Core3とかで使っているAXP2101を乗せる基板上の空きがなかったのかも?
ですねー
>Core3とかで使っているAXP2101を乗せる基板上の空きがなかったのかも?
えー そーなの!?
ありそう…
S3搭載した新型を準備中なのでそっちに期待、、、
すみません.M5Unifiedを使ってM5StickCPlus2でコンパイルを行おうとしたところ,M5Unified.cpp関連のエラーが出てしまったのですが,対処法をご存知でしょうか?
サンプルプログラムすらコンパイルできない状況ではまっています.
IDE: Arduino
ボードマネージャー: ESP標準のもの
https://espressif.github.io/arduino-esp32/package_esp32_index.json
ライブラリ: M5GFX 0.2.9 M5Unified 0.2.7
エラーログ
M5Unified.cpp:755:15: error: ‘touch_pad_init’ was not declared in this scope; did you mean ‘touch_pad_t’?
755 | touch_pad_init();
| ^~~~~~~~~~~~~~
| touch_pad_t
Arduino/libraries/M5Unified/src/M5Unified.cpp:760:15: error: ‘touch_pad_deinit’ was not declared in this scope; did you mean ‘touch_pad_t’?
760 | touch_pad_deinit();
| ^~~~~~~~~~~~~~~~
| touch_pad_t
バージョンを変えるなどいろいろ試してみたのですがうまくいきませんでした.
解決しました.
https://static-cdn.m5stack.com/resource/arduino/package_m5stack_index.json
のボードマネージャーを追加して,こちらのm5stick C Plus2 のボードを選択したところコンパイルできました.
いつもお世話になっております。
“`
static void updateBatteryInfo() {
const float battery_alpha = 0.05f; // 0.05~0.2
// 生のバッテリー値を取得
int level = M5.Power.getBatteryLevel();
// フィルタ済み値(初回は level で初期化)
static float batteryLevelFiltered = (float)level;
// EMA(指数移動平均)
batteryLevelFiltered =
batteryLevelFiltered * (1.0f – battery_alpha) +
level * battery_alpha;
// 結果を格納
dev.batteryLevel = (int)batteryLevelFiltered;
dev.batteryVoltage = M5.Power.getBatteryVoltage();
dev.batteryCharging = M5.Power.isCharging();
}
“`
Plus2のバッテリーレベルが安定しないので、上記のように指数移動平均(EMA)を
使っています。充電の有無を**M5.Power.isCharging();**で調べていますが、
反映されません。
解決策はありますか?
残念ながらPlus2はバッテリーの電圧を分割して測定しているだけのものなので、充電中かを確認することができません。
なのでis_charging_t::charge_unknown(2)がisChargingから返却されている気がします。
ちなみにM5StickS3がおそらく今週金曜日に販売開始される予定です
そちらであれば充電のON/OFFと充電中かが取得できそうな感じでした
https://docs.m5stack.switch-science.com/en/core/StickS3
解説をありがとうございます。
了解しました。それらしい関数があったので、正しい値が得られると
思っていました。
後継機種も調べてみます。
今まで M5.Power.isCharging()はbool型で、0,1,2を返すとは考えて
いませんでした。
今回のアドバイスで、正しい表示(unknown)に表示できました。
感謝いたします。
お世話になります。
こちらで公開されているサンプルプログラム(RTCによる起床)を試して
みました。
Aボタンを押すと、LCDは消灯しますが、RTCによる起床が期待とは
異なります。
USBケーブルを接続しているとPWRボタンを押さないと起床せず、
USBケーブルを接続していない場合も、1分待っても起床しません。
何か勘違いしてるのでしょうか。
勘違いしていました。
USBケーブルを接続しない状態で Aボタンを押しスリープさせると
1分後に起床しました。
USBケーブルを接続の有無が影響するようです。
また、フォントが小さすぎて読めないので、以下のように変更し
ました。
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.setRotation(1);
M5.Display.setTextFont(&fonts::Font4);
M5.Display.clear(TFT_RED);
M5.delay(500); // Serial出力用のウエイト
これなら、十分に読めます(4倍の大きさ?)。
動いてよかったです
Plus2とかInk系の電力管理はUSB接続していると全部無視されちゃうんですよね
接続したまま変な状態にいれてしまうと、接続を取ってから再接続とかしないと復帰しないモードもあった気がします
【報告】
hsenshuです。興味深い工夫を思いついたので紹介します。
M5StickC Plus2の赤外線LEDは大電流が流れるので、ON/OFFさせるには
注意が必要です。
でも、それを活用する方法を見つけました。
安価なモバイルバッテリーを、以下の方法でACアダプタ的に使えるよう
になります。
【モバイルバッテリーのオートパワーオフ キャンセラーへの応用】
モバイルバッテリーの多くはスマホ等の大電流を充電する機器
向けに設計されており、100mA 未満の低電流機器の充電は考慮
されていない。
それどころか、低電流時には充電を停止する機能(オートパワー
オフ)が備わっている。
この機能により M5StickC Plus2 への充電はできないことが多い
(この機能を無効にできるモバイルバッテリーも市販されている
が高価)。
そこで、Plus2 内の赤外線 LED を定期的(10数秒周期)に 0.1秒
程度点灯する機能を実装した。
赤外線 LED の点灯時は 一瞬だが、 Plus2 全体で 100mA を超える
電流が流れる。
この動作で、モバイルバッテリーには「充電先は充電を希望してい
る」と判断させ、給電を持続させる。
この機能による消費電力は増加するが、80mA*1/100=1mAh 程度
の増加である。例を挙げると、3日間の連続使用で 72mAh 程度、
5,000~ 10,000 mAh モバイルバッテリーにとっては 2% 未満の
増加である。
※ モバイルバッテリーによっては、点滅周期はもっと長くともOk
かもしれない。
赤LEDも同時に点灯するが、これは動作確認用のモニタである。
この機能によって、多くの一般的なモバイルバッテリーにて充電が
可能になった。携帯性を維持できるため、利用拡大が考えられる。