はじめに
今回はM5stamp C3UとRaspberry Pi 4BとでBLE通信を行うプログラムを実施します。以前投稿したM5stick c PlusとRaspberry Pi 4B間をBLE通信するプログラムをM5stick c Plus部分のプログラムをM5stamp C3Uに置き換える形で改造します。
以前投稿したM5stick c PlusとRaspberry Pi 4B間BLE通信プログラム
https://fa-vivorock-mura.com/m5stick-c-plus-ble-ver240224/
前回ENV IIIセンサからの情報をI2C通信によってM5stamp C3Uで受信およびシリアルモニタへの表示するプログラムを作成しました。実は今回のM5stamp C3UのBLEプログラムのほうが先に始めたもので、前回のI2Cプログラムはデバックが目的でした。
前回のM5stamp C3UとENV IIIセンサプログラム
https://fa-vivorock-mura.com/m5stamp-c3u-2/
経緯としては
今回のプログラミングがうまくいかない。ENV IIIセンサからの情報がRaspberry Pi 4B側で受信できないどころか、そもそもM5stamp C3Uの存在を認知しない。BLE?それともENV IIIセンサプログラムに問題?
→プログラムのどこまで実行できているか確認するため、シリアルモニタに確認用文字列を送信するプログラムを追加。
→ENV IIIセンサにかかわるプログラム前後でシリアルモニタに文字列が送信されない事象を発見。
→試しにENV IIIセンサにかかわるプログラムをすべてコメントで無効化したところRaspberry Pi 4B側でM5stamp C3Uを認知するようになった。
→つまりBLE通信プログラムに問題はなく、ENV IIIセンサプログラムに問題があると判明。BLE通信は一度置いておいて、先にM5stamp C3UとENV IIIセンサ間I2C通信のみに限定したプログラミングを実施。
こんな感じの経緯です。
私自身の備忘録のために、今回のデバックの流れについても記録します。
使用するもの
M5stamp C3U
|
(まとめ買い用)
|
Raspberry Pi 4B
|
ENV IIIセンサ
|
M5stamp C3U側の接続は下写真のようになりました。
M5stamp C3UとENV IIIセンサとの接続ピンは前回の投稿を確認してください。
プログラム
M5stamp C3U側のプログラムは以下のようになりました。
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <esp_sleep.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#define LED_PIN 2
#include <Adafruit_Sensor.h>
#include "M5UnitENV.h"
Adafruit_NeoPixel pixels(1, LED_PIN, NEO_GRB + NEO_KHZ800);
#define T_PERIOD 10 // アドバタイジングパケットを送る秒数
#define S_PERIOD 20 // Deep Sleepする秒数
SHT3X sht3x;
QMP6988 qmp;
float temperature = 0.0;
float humidity = 0.0;
float pressure = 0.0;
RTC_DATA_ATTR static uint8_t seq; // 送信SEQ
// BLEデータを設定する関数
void setAdvData(BLEAdvertising *pAdvertising, float temperature, float humidity, float pressure) {
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
// アドバタイジングパケットの設定
oAdvertisementData.setFlags(0x06);
std::string strServiceData = "";
strServiceData += (char)0x0a;
strServiceData += (char)0xff;
strServiceData += (char)0xff;
strServiceData += (char)0xff;
strServiceData += (char)seq;
strServiceData += (char)((uint16_t)(temperature * 100) & 0xFF);
strServiceData += (char)(((uint16_t)(temperature * 100) >> 8) & 0xFF);
strServiceData += (char)((uint16_t)(humidity * 100) & 0xFF);
strServiceData += (char)(((uint16_t)(humidity * 100) >> 8) & 0xFF);
strServiceData += (char)((uint16_t)(pressure / 100) & 0xFF);
strServiceData += (char)(((uint16_t)(pressure / 100) >> 8) & 0xFF);
oAdvertisementData.addData(strServiceData);
pAdvertising->setAdvertisementData(oAdvertisementData);
}
void setup() {
Serial.begin(115200);
//つまづきポイント開始
Wire.begin(8, 9);
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L, 8, 9, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) delay(1);
}
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR, 8, 9, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) delay(1);
}
//つまづきポイント終了
pressure = qmp.calcPressure();
if(sht3x.update()){
temperature = sht3x.cTemp;
humidity = sht3x.humidity;
}
BLEDevice::init("blepub-c3u");
BLEServer *pServer = BLEDevice::createServer();
BLEAdvertising *pAdvertising = pServer->getAdvertising();
setAdvData(pAdvertising, temperature, humidity, pressure); // BLEデータを設定
pAdvertising->start();
delay(T_PERIOD * 1000);
pAdvertising->stop();
seq++;
delay(10);
esp_deep_sleep(1000000LL * S_PERIOD);
}
void loop() {
// このプログラムではloop()は使用されないので、何も記述しない
}
M5stick c Plus側のプログラムをM5stamp C3Uに合うように改造しました。
M5stick c Plus側ではセンサーとの通信に問題が生じたら通信不良を画面に表示させるプログラムでしたが、今回はシリアルモニタに表示させるようにしています。
Raspberry pi 4B側のプログラムは特に変更はありません。
つまづき備忘録
今回のつまづきポイントについて記録します。先にうまくつまづいた原因と理由を報告します。
前回の投稿でも述べたように私の勘違いによって、上記プログラムのつまづきポイント開始から終了までの部分を以下のように間違えてプログラミングしていたことが原因でした。
Wire.begin();
qmp.begin();
sht3x.begin();
当初はBLEプログラムを疑っていたため、void setup()内のプログラムに「Starting BLE work!」「Ending BLE work!」という文字列をシリアルモニタに表示させるプログラムを追加してどこでプログラムが止まっているかを調べました。その時のvoid setup()のプログラムがこちらです。
//検証の際使用したプログラム
void setup() {
Serial.begin(115200);
//挿入ポイント1
//Serial.println("Starting BLE work!");
Wire.begin();
qmp.begin();
sht3x.begin();
pressure = qmp.calcPressure();
if(sht3x.update()){
temperature = sht3x.cTemp;
humidity = sht3x.humidity;
}
//挿入ポイント2
//Serial.println("Starting BLE work!");
BLEDevice::init("blepub-c3u");
BLEServer *pServer = BLEDevice::createServer();
BLEAdvertising *pAdvertising = pServer->getAdvertising();
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
// アドバタイジングパケットの設定
oAdvertisementData.setFlags(0x06);
std::string strServiceData = "";
strServiceData += (char)0x0a;
strServiceData += (char)0xff;
strServiceData += (char)0xff;
strServiceData += (char)0xff;
strServiceData += (char)seq;
strServiceData += (char)((uint16_t)(temperature * 100) & 0xFF);
strServiceData += (char)(((uint16_t)(temperature * 100) >> 8) & 0xFF);
strServiceData += (char)((uint16_t)(humidity * 100) & 0xFF);
strServiceData += (char)(((uint16_t)(humidity * 100) >> 8) & 0xFF);
strServiceData += (char)((uint16_t)(pressure / 100) & 0xFF);
strServiceData += (char)(((uint16_t)(pressure / 100) >> 8) & 0xFF);
oAdvertisementData.addData(strServiceData);
pAdvertising->setAdvertisementData(oAdvertisementData);
pAdvertising->start();
delay(T_PERIOD * 1000);
pAdvertising->stop();
seq++;
delay(10);
//挿入ポイント3
//Serial.println("Ending BLE work!");
esp_deep_sleep(1000000LL * S_PERIOD);
}
挿入ポイント1と2交互にSerial.println("Starting BLE work!");
を入れ、挿入ポイント3にSerial.println("Ending BLE work!");
を入れました。
挿入ポイント1, 2をそれぞれ実行し、ポイント2の時にシリアルモニタに文字列が表示されないことを発見、ポイント3についてはどちらでも表示されませんでした。それでENV IIIセンサにかかわるプログラムをすべて無効化したところ、ポイント3の文字列までコメント表示されるようになり、この時にRaspberry Pi 4B側でM5stamp C3Uの存在を認知するようになりました。つまりBLEプログラムに問題はなくセンサとのやり取りに問題があることがわかりました。
ようやくプログラムの問題箇所が判明し、前回の投稿のプログラミングに至ったというわけです。
プログラム実行結果
実行結果はM5stick c Plus⇔Raspberry Pi 4B間BLE通信と一緒のため割愛します。M5stick c PlusをM5stamp C3Uに置き換えることに成功しました。5V昇圧回路と乾電池を使用したスタンドアロン状態でも送信できることを確認しています。
感想
私自身マイコンとC言語を使用したプログラミング経験が浅く、このようなデバック手法を知りませんでした。また横着してBLE通信プログラムから実施したため、I2Cプログラム部分に問題があることを見抜けませんでした。
Deep sleepに対応していないのではないかと考え、その部分を無効化したり、すべてvoid setup()内に書き込んでいるのが悪いのではないかと思い、void loop()側にもプログラムを振り分けてみたり、chat GPTに問題箇所を特定してもらおうとか、最初のうちは考え付く限りの修正をトライアンドエラーで何度も実施するありさまでした。シリアルモニタにコメントを表示させるデバック手法について知ったのはchat GPTにプログラムの検証を依頼したときに知りました。chat GPTでは問題箇所の特定はできなかったのですが、Serial.println("コメント");
を使用するアイディアをもらえたのは大きかったです。
今後はシリアルモニタを活用してデバックの効率化と、横着せずに1歩ずつ段階を踏んでプログラムしていきます。