いつの間にかPlatformIOでM5Core2がサポートされていた(以前は他のボード設定を流用する必要があった)ので、設定方法をまとめる。
また、次のようなサンプルコードで動作を確認する。
・サンプルコード1: 画面表示とバッテリー情報読み出し
・サンプルコード2: Wifi接続とNTPによるRTCへの時刻設定
・サンプルコード3: GPIOとADC
・サンプルコード4: マイクロSDへのデータ書き込み
動作確認はWindows 10とVSCodeを使用している。
参考資料
・公式ページ(回路図等はここから)
・Arduinoライブラリ(大体の使い方はM5Core2/examples/でわかる)
・最初に書き込まれているFactory test image
・MCUとして使用されているESP32-D0WDQ6-V3のデータシート
M5Core2用プロジェクトの作り方
PlatformIOのインストールとプロジェクト生成
VSCodeの拡張機能からPlatformIOをインストールし、VSCodeを再起動する。
PIO HomeからProjects & Configurationを選択し、Create New Projectをクリック。
Project Wizardが表示されるので、任意のプロジェクト名を設定し、BoardはM5Stack Core2を選択し、Finishi。
(なぜかプロジェクト生成に結構時間がかかった。)
この画面のようにプロジェクトファイルが生成されればOK。後のサンプルではsrc/main.cppを編集する。
必要なライブラリのインストール
次に、生成したプロジェクトに必要なライブラリを関連付ける。
PIO HomeからLibrariesでM5Core2を検索する。
M5Core2を選択し、Add to Projectをクリック。
2つめのドロップダウンリストから、先程生成したプロジェクトを選択し、Addをクリック。
もうひとつ、画面表示用ライブラリのLovyanGFXも追加する。手順はM5Core2ライブラリの追加と同じ。(画面表示をデフォルトのライブラリで操作する場合は不要。)
ここまでの設定が正しく出来ていれば、最初に生成したプロジェクト内のplatformio.iniがこのように変化している。
ビルドと実機への書き込み
準備
M5Stack公式からCP2104 Driverをダウンロードしてインストール(環境によってはなくても動くかも?)。
今回の環境は64bit版Windows 10なのでCP210xVCPInstaller_x64_v6.7.0.0.exeをインストールした。
M5Core2をUSBケーブルでPCに接続、電源をONにする。
サンプルコード1(画面表示とバッテリー情報読み出し)
バッテリ電圧と電流値をライブラリで読み出し、LovyanGFXを使って画面に表示する。
充電中は常時表示、バッテリー動作中は約500ミリ秒で画面ONとOFFを繰り返す。
#include <M5Core2.h>
#define LGFX_M5STACK_CORE2
#include <LovyanGFX.hpp>
static LGFX lcd;
void setup()
{
M5.begin(true, true, true, true);
lcd.init();
lcd.setFont(&fonts::lgfxJapanGothic_28);
lcd.setBrightness(128);
lcd.setCursor(0, 128);
lcd.println("はろーテスト壱");
}
void loop()
{
M5.update();
float batVol = M5.Axp.GetBatVoltage();
float batCur = M5.Axp.GetBatCurrent();
lcd.setCursor(0, 156);
lcd.printf("Baterry: %1.3f\nCurrent: %.3f\n", batVol, batCur);
if (batCur < 0) {
delay(500);
lcd.setBrightness(0);
lcd.sleep();
delay(500);
lcd.wakeup();
lcd.setBrightness(128);
}
}
このコードをプロジェクトのsrc/main.cppに置き、ビルドしてみる。
PlatformIOのアイコンをクリックして、PROJECT TASKSからm5stack-core2→General→Build、ターミナルでSUCCESSが表示されることを確認。
今度はUpload(エラーが出る場合はM5Stack側の電源が入っていること、USBが接続されていることを確認)。
書き込みが完了すると自動でリセットがかかり、この画面が表示される。
ひらがな、カタカナ、漢字も表示できるが、機種依存文字はバグる。
バッテリが正常なら電流値は正の値(充電電流?)になる。バッテリが抜けていると電流値は0。
USBケーブルを外すと電流値が負の値(消費電流)になる。
サンプルコード2(Wifi接続とNTPによるRTCへの時刻設定)
M5Core2はRTCを持っているので、時刻を設定すれば電源OFF時やDeep Sleep時に時刻が保持される。
Wifi接続し、NTPを使って時刻同期する。
※こちらの記事を参考にしました。
https://qiita.com/kaoruka/items/b36c99fb124fea0d5d24
#include <M5Core2.h>
#include <time.h>
#include <WiFi.h>
#define LGFX_M5STACK_CORE2
#include <LovyanGFX.hpp>
static LGFX lcd;
char ssid[] = "*****************";
char pass[] = "*****************";
const char* ntpServer = "ntp.jst.mfeed.ad.jp";
const long gmtOffset_sec = 9 * 3600; // Offset: +9Hour (JST)
const int daylightOffset_sec = 0; // Summer time = no
static const char *wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
RTC_DateTypeDef RTC_DateStruct;
RTC_TimeTypeDef RTC_TimeStruct;
void setNTP2RTC(){
struct tm timeinfo;
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
while (!getLocalTime(&timeinfo)) {
delay(1000);
}
//Over write
M5.Rtc.GetDate(&RTC_DateStruct);
RTC_DateStruct.Year = timeinfo.tm_year + 1900;
RTC_DateStruct.Month = timeinfo.tm_mon + 1;
RTC_DateStruct.Date = timeinfo.tm_mday;
RTC_DateStruct.WeekDay = timeinfo.tm_wday;
M5.Rtc.SetDate(&RTC_DateStruct);
//Over write
M5.Rtc.GetTime(&RTC_TimeStruct);
RTC_TimeStruct.Hours = timeinfo.tm_hour;
RTC_TimeStruct.Minutes = timeinfo.tm_min;
RTC_TimeStruct.Seconds = timeinfo.tm_sec;
M5.Rtc.SetTime(&RTC_TimeStruct);
}
int wifiConnect() {
int con_count = 0;
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
lcd.print(".");
con_count++;
if(con_count >= 20){
break;
}
}
if(con_count < 20){
lcd.print("\nWiFi connected.");
}else{
lcd.print("\nWiFi did not connect.");
}
lcd.print("\nIP=");
lcd.print(WiFi.localIP());
return con_count;
}
void setup() {
M5.begin(true, true, true, true);
lcd.init();
lcd.setFont(&fonts::lgfxJapanGothic_28);
lcd.setBrightness(128);
lcd.setCursor(0, 0);
if (wifiConnect() < 20) {
setNTP2RTC();
}
}
void loop() {
lcd.setCursor(0, 100);
M5.Rtc.GetDate(&RTC_DateStruct);
M5.Rtc.GetTime(&RTC_TimeStruct);
lcd.printf("%04d.%02d.%02d %s\n", RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date, wd[RTC_DateStruct.WeekDay]);
lcd.printf("%02d:%02d:%02d", RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);
delay(500);
}
コード内のssid[]とpass[]には、接続したいWifiのSSIDとパスワードを設定すること。
5GHz帯の無線LANには接続できなかった。ESP32-D0WDQ6-V3のデータシートに以下の記載があるので2.4GHz帯でないとダメかもしれない。
• 802.11 b/g/n
• 802.11 n (2.4 GHz), up to 150 Mbps
Wifiに接続できるとNTPで時刻が同期され、以下の画面が表示される。
サンプルコード3(GPIOとADC)
GPIOとADCはよくあるArduinoと同じ書き方で制御できる。
(GPIOの35と36はもともとADC用に設定されているが、他のGPIOをADCに設定する場合は追加で何か設定が要るかも知れない。未調査。)
#include <M5Core2.h>
#define LGFX_M5STACK_CORE2
#include <LovyanGFX.hpp>
static LGFX lcd;
void setup() {
M5.begin(true, true, true, true);
lcd.init();
lcd.setFont(&fonts::lgfxJapanGothic_28);
lcd.setBrightness(128);
lcd.setCursor(0, 0);
//Initialize GPIO
pinMode(19, OUTPUT);
digitalWrite(19, LOW);
}
void loop() {
uint16_t pin35ADC = analogRead(35);
float pin35vol = (float)pin35ADC * 3.3 / 4096;
uint16_t pin36ADC = analogRead(36);
float pin36vol = (float)pin36ADC * 3.3 / 4096;
lcd.setCursor(0, 100);
lcd.printf("ADC35: %1.4f[V]\nADC36: %1.4f[V]\n", pin35vol, pin36vol);
//Toggle GPIO
digitalWrite(19, LOW);
delay(400);
digitalWrite(19, HIGH);
delay(100);
}
動作画面。自作拡張ボードでADCに信号入力している。(拡張ボードの話は別記事で書くかも。)
サンプルコード4(マイクロSDへのデータ書き込み)
マイクロSDカードは、公式には16GBまで対応している。実験した範囲では32GBも認識した。
マイクロSDカードはあらかじめFAT32でフォーマットしておく。
以下のサンプルは、
・setupでRTCから時刻を読み出し、YYYYMMDD_HHMMSS.csvという命名規則でファイル生成
・loopではRTCから定期的に時刻を読み出して上記のファイルに追記
という動作をしている。
※あらかじめサンプルコード2でRTCを設定しておく必要がある。
#include <M5Core2.h>
#define LGFX_M5STACK_CORE2
#include <LovyanGFX.hpp>
static LGFX lcd;
RTC_TimeTypeDef RTCtime_Now;
RTC_DateTypeDef RTCDate_Now;
char fileStrbuff[64];
char timeStrbuff[64];
File file;
void setup()
{
M5.begin(true, true, true, true);
lcd.init();
lcd.setFont(&fonts::lgfxJapanGothic_28);
lcd.setBrightness(128);
//SDカードが存在するかチェック
if (!SD.begin()) {
lcd.println("ERROR: SD CARD.");
while (1);
}
//RTCで起動した時刻を取得し、ファイル名に使用する
M5.Rtc.GetTime(&RTCtime_Now);
M5.Rtc.GetDate(&RTCDate_Now);
//ファイルは絶対パスで指定するので、/(ファイル名)、/が無いとエラーになる
sprintf(fileStrbuff,"/%04d%02d%02d_%02d%02d%02d.csv",
RTCDate_Now.Year,RTCDate_Now.Month,RTCDate_Now.Date,
RTCtime_Now.Hours,RTCtime_Now.Minutes,RTCtime_Now.Seconds
);
//ファイル生成(チェック)
file = SD.open(fileStrbuff, FILE_WRITE);
if (!file) {
lcd.println("ERROR: OPEN FILE.");
while (1);
}
file.close();
}
void loop()
{
M5.update();
//現在時刻取得
M5.Rtc.GetTime(&RTCtime_Now);
M5.Rtc.GetDate(&RTCDate_Now);
sprintf(timeStrbuff,"%04d/%02d/%02d %02d:%02d:%02d",
RTCDate_Now.Year,RTCDate_Now.Month,RTCDate_Now.Date,
RTCtime_Now.Hours,RTCtime_Now.Minutes,RTCtime_Now.Seconds
);
//生成したファイルが存在することを確認し、タイムスタンプを書き込み
//安全対策で毎回ファイルを開いて閉じているが、別途クローズ処理するならsetupでオープンしたまま書き込み続けて良い
if (SD.exists(fileStrbuff)) {
file = SD.open(fileStrbuff, FILE_APPEND);
file.println(timeStrbuff);
file.close();
} else {
//途中でSDが抜けたor認識エラー
lcd.println("ERROR: FILE IS MISSING!");
while (1);
}
lcd.setCursor(0, 100);
lcd.println(timeStrbuff);
delay(1000);
}
マイクロSDが正しく挿さっていれば、この画面のように時刻を表示し、同時にデータを書き込み続ける。
一度電源を切ってSDカードを抜き、PCで確認するとこのようなファイルが生成されている。