ハード未接続に対応
This commit is contained in:
parent
b8eb7bafe8
commit
2e3cd4220a
|
|
@ -74,65 +74,70 @@ private:
|
||||||
SensorLogger* logger_;
|
SensorLogger* logger_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void SensorLogger::begin(uint8_t soilPin) {
|
void SensorLogger::begin(uint8_t soilPin, bool i2cConnected, bool bleDataConnected) {
|
||||||
// センサーとBLE通信を使い始めるための初期設定です。
|
// センサーとBLE通信を使い始めるための初期設定です。
|
||||||
soilPin_ = soilPin;
|
soilPin_ = soilPin;
|
||||||
|
bleDataConnected_ = bleDataConnected;
|
||||||
pinMode(soilPin_, INPUT);
|
pinMode(soilPin_, INPUT);
|
||||||
analogReadResolution(12);
|
analogReadResolution(12);
|
||||||
|
|
||||||
// センサーが接続されているときだけ初期化します。
|
// センサーが接続されているときだけ初期化します。
|
||||||
// 未接続時にライブラリへアクセスすると、不要なI2Cエラーや待ち時間が発生します。
|
// 未接続時にライブラリへアクセスすると、不要なI2Cエラーや待ち時間が発生します。
|
||||||
if (isI2cDevicePresent(0x38)) {
|
if (i2cConnected) {
|
||||||
aht20Ready = aht20.begin();
|
if (isI2cDevicePresent(0x38)) {
|
||||||
if (!aht20Ready) {
|
aht20Ready = aht20.begin();
|
||||||
Serial.println("[ERROR] AHT20 initialization failed");
|
if (!aht20Ready) {
|
||||||
|
Serial.println("[ERROR] AHT20 initialization failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Serial.println("[WARNING] AHT20 not detected at 0x38");
|
||||||
}
|
}
|
||||||
} else {
|
if (isI2cDevicePresent(0x23)) {
|
||||||
Serial.println("[WARNING] AHT20 not detected at 0x38");
|
bh1750Ready = bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
|
||||||
}
|
if (!bh1750Ready) {
|
||||||
if (isI2cDevicePresent(0x23)) {
|
Serial.println("[ERROR] BH1750 initialization failed");
|
||||||
bh1750Ready = bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
|
}
|
||||||
if (!bh1750Ready) {
|
} else {
|
||||||
Serial.println("[ERROR] BH1750 initialization failed");
|
Serial.println("[WARNING] BH1750 not detected at 0x23");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Serial.println("[WARNING] BH1750 not detected at 0x23");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ESP32固有の番号を名前に加え、同じ名前の機器と区別します。
|
if (bleDataConnected_) {
|
||||||
uint64_t mac = ESP.getEfuseMac();
|
// ESP32固有の番号を名前に加え、同じ名前の機器と区別します。
|
||||||
char name[14];
|
uint64_t mac = ESP.getEfuseMac();
|
||||||
snprintf(name, sizeof(name), "%s-%05llX", kBleDeviceNamePrefix, mac & 0xFFFFF);
|
char name[14];
|
||||||
bluetoothName_ = name;
|
snprintf(name, sizeof(name), "%s-%05llX", kBleDeviceNamePrefix, mac & 0xFFFFF);
|
||||||
BLEDevice::init(bluetoothName_.c_str());
|
bluetoothName_ = name;
|
||||||
Serial.printf("[INFO] BLE initialized as '%s'\n", bluetoothName_.c_str());
|
BLEDevice::init(bluetoothName_.c_str());
|
||||||
|
Serial.printf("[INFO] BLE initialized as '%s'\n", bluetoothName_.c_str());
|
||||||
|
|
||||||
// BLEサーバーと、センサー通信用のサービスを作ります。
|
// BLEサーバーと、センサー通信用のサービスを作ります。
|
||||||
server_ = BLEDevice::createServer();
|
server_ = BLEDevice::createServer();
|
||||||
server_->setCallbacks(new SensorLoggerServerCallbacks(this));
|
server_->setCallbacks(new SensorLoggerServerCallbacks(this));
|
||||||
BLEService* service = server_->createService(kNusServiceUuid);
|
BLEService* service = server_->createService(kNusServiceUuid);
|
||||||
// RXはアプリからESP32へ命令を送る通路です。
|
// RXはアプリからESP32へ命令を送る通路です。
|
||||||
BLECharacteristic* rxCharacteristic = service->createCharacteristic(
|
BLECharacteristic* rxCharacteristic = service->createCharacteristic(
|
||||||
kNusRxUuid, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
|
kNusRxUuid, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
|
||||||
rxCharacteristic->setCallbacks(new SensorLoggerCallbacks(this));
|
rxCharacteristic->setCallbacks(new SensorLoggerCallbacks(this));
|
||||||
// TXはESP32からアプリへログを送る通路です。
|
// TXはESP32からアプリへログを送る通路です。
|
||||||
txCharacteristic_ = service->createCharacteristic(
|
txCharacteristic_ = service->createCharacteristic(
|
||||||
kNusTxUuid, BLECharacteristic::PROPERTY_NOTIFY);
|
kNusTxUuid, BLECharacteristic::PROPERTY_NOTIFY);
|
||||||
txCharacteristic_->addDescriptor(new BLE2902());
|
txCharacteristic_->addDescriptor(new BLE2902());
|
||||||
service->start();
|
service->start();
|
||||||
|
|
||||||
// BLE広告は「この機器が近くにいます」と周囲へ知らせる仕組みです。
|
// BLE広告は「この機器が近くにいます」と周囲へ知らせる仕組みです。
|
||||||
BLEAdvertising* advertising = BLEDevice::getAdvertising();
|
BLEAdvertising* advertising = BLEDevice::getAdvertising();
|
||||||
advertising->addServiceUUID(kNusServiceUuid);
|
advertising->addServiceUUID(kNusServiceUuid);
|
||||||
advertising->setScanResponse(true);
|
advertising->setScanResponse(true);
|
||||||
// A2DP開始後に共通のBluetooth名が変わっても、BLE名を返せるようにします。
|
// A2DP開始後に共通のBluetooth名が変わっても、BLE名を返せるようにします。
|
||||||
BLEAdvertisementData scanResponseData;
|
BLEAdvertisementData scanResponseData;
|
||||||
scanResponseData.setName(bluetoothName_.c_str());
|
scanResponseData.setName(bluetoothName_.c_str());
|
||||||
advertising->setScanResponseData(scanResponseData);
|
advertising->setScanResponseData(scanResponseData);
|
||||||
advertising->setMinPreferred(0x06);
|
advertising->setMinPreferred(0x06);
|
||||||
advertising->setMinPreferred(0x12);
|
advertising->setMinPreferred(0x12);
|
||||||
BLEDevice::startAdvertising();
|
BLEDevice::startAdvertising();
|
||||||
Serial.println("[INFO] BLE advertising started");
|
Serial.println("[INFO] BLE advertising started");
|
||||||
|
}
|
||||||
|
|
||||||
// 起動直後にも1回計測して、画面に値を表示できるようにします。
|
// 起動直後にも1回計測して、画面に値を表示できるようにします。
|
||||||
readAndStore();
|
readAndStore();
|
||||||
|
|
@ -142,10 +147,13 @@ void SensorLogger::begin(uint8_t soilPin) {
|
||||||
|
|
||||||
bool SensorLogger::update() {
|
bool SensorLogger::update() {
|
||||||
// loopから繰り返し呼ばれ、通信と定期計測を担当します。
|
// loopから繰り返し呼ばれ、通信と定期計測を担当します。
|
||||||
processBluetooth();
|
if (bleDataConnected_) {
|
||||||
|
processBluetooth();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
if (!bluetoothConnected_ && now - lastAdvertise_ >= kAdvertiseRetryIntervalMs) {
|
if (bleDataConnected_ && !bluetoothConnected_
|
||||||
|
&& now - lastAdvertise_ >= kAdvertiseRetryIntervalMs) {
|
||||||
// 接続されていなければ、定期的にBLE広告を出し直します。
|
// 接続されていなければ、定期的にBLE広告を出し直します。
|
||||||
lastAdvertise_ = now;
|
lastAdvertise_ = now;
|
||||||
BLEDevice::startAdvertising();
|
BLEDevice::startAdvertising();
|
||||||
|
|
@ -288,6 +296,10 @@ void SensorLogger::processBluetooth() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SensorLogger::receiveBluetoothData(const String& data) {
|
void SensorLogger::receiveBluetoothData(const String& data) {
|
||||||
|
if (!bleDataConnected_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// BLEで届いた文字を命令バッファへ追加します。
|
// BLEで届いた文字を命令バッファへ追加します。
|
||||||
for (size_t index = 0; index < data.length(); ++index) {
|
for (size_t index = 0; index < data.length(); ++index) {
|
||||||
char character = data[index];
|
char character = data[index];
|
||||||
|
|
@ -320,6 +332,10 @@ void SensorLogger::applyHistoricalTimeAdjustment(int64_t epoch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SensorLogger::processCommand(const String& command) {
|
void SensorLogger::processCommand(const String& command) {
|
||||||
|
if (!bleDataConnected_ || txCharacteristic_ == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// アプリから届いた命令を判定します。
|
// アプリから届いた命令を判定します。
|
||||||
if (command == "GET_LOG") {
|
if (command == "GET_LOG") {
|
||||||
// 保存済みのログを順番に送ります。
|
// 保存済みのログを順番に送ります。
|
||||||
|
|
@ -366,6 +382,10 @@ void SensorLogger::processCommand(const String& command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SensorLogger::sendLog() {
|
void SensorLogger::sendLog() {
|
||||||
|
if (!bleDataConnected_ || txCharacteristic_ == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 保存したログを1行ずつ作り、BLE通知で送ります。
|
// 保存したログを1行ずつ作り、BLE通知で送ります。
|
||||||
for (size_t index = 0; index < logCount_; ++index) {
|
for (size_t index = 0; index < logCount_; ++index) {
|
||||||
size_t logIndex = (logStart_ + index) % kMaxLogEntries;
|
size_t logIndex = (logStart_ + index) % kMaxLogEntries;
|
||||||
|
|
@ -390,6 +410,10 @@ void SensorLogger::sendLog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SensorLogger::sendNotification(const char* data) {
|
void SensorLogger::sendNotification(const char* data) {
|
||||||
|
if (!bleDataConnected_ || txCharacteristic_ == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// BLE通知には一度に送れる長さの制限があるため、短く分割します。
|
// BLE通知には一度に送れる長さの制限があるため、短く分割します。
|
||||||
constexpr size_t kNotificationSize = 20;
|
constexpr size_t kNotificationSize = 20;
|
||||||
uint8_t notification[kNotificationSize];
|
uint8_t notification[kNotificationSize];
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ public:
|
||||||
static constexpr size_t kMaxLogEntries = 2000;
|
static constexpr size_t kMaxLogEntries = 2000;
|
||||||
|
|
||||||
// センサー、BLE、保存データを使い始める準備をします。
|
// センサー、BLE、保存データを使い始める準備をします。
|
||||||
void begin(uint8_t soilPin);
|
void begin(uint8_t soilPin, bool i2cConnected, bool bleDataConnected);
|
||||||
// BLEの受信と、一定時間ごとの計測を行います。
|
// BLEの受信と、一定時間ごとの計測を行います。
|
||||||
bool update();
|
bool update();
|
||||||
// センサーを読み取り、画面表示用の最新値を更新します。
|
// センサーを読み取り、画面表示用の最新値を更新します。
|
||||||
|
|
@ -69,6 +69,7 @@ private:
|
||||||
bool timeSynchronized_ = false; // 時刻合わせが済んだか
|
bool timeSynchronized_ = false; // 時刻合わせが済んだか
|
||||||
bool historicalAdjustmentApplied_ = false; // 過去ログの補正が済んだか
|
bool historicalAdjustmentApplied_ = false; // 過去ログの補正が済んだか
|
||||||
bool bluetoothConnected_ = false; // BLE接続中か
|
bool bluetoothConnected_ = false; // BLE接続中か
|
||||||
|
bool bleDataConnected_ = false; // BLEデータ送受信を使うか
|
||||||
String bluetoothName_; // BLEに表示する名前
|
String bluetoothName_; // BLEに表示する名前
|
||||||
String commandBuffer_; // BLEから受け取った未処理の文字
|
String commandBuffer_; // BLEから受け取った未処理の文字
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
// I2C機器(OLED・センサー)を接続していない場合は false にします。
|
||||||
|
const bool kI2cConnected = true; // I2C機器が接続されていない場合は false にします。
|
||||||
|
// BLEでのセンサーデータ送受信を使わない場合は false にします。
|
||||||
|
const bool kBleDataConnected = true; // BLEでのセンサーデータ送受信を使わない場合は false にします。
|
||||||
|
// NeoPixelの接続ピンです。未接続の場合は -1 にすると一切使用しません。
|
||||||
|
const int kNeoPixelPin = 18; // 使用しない場合は -1 に、通常は 18番ピンに接続します。
|
||||||
|
|
||||||
// A2DPは、スマートフォンから音楽を受信するBluetoothの仕組みです。
|
// A2DPは、スマートフォンから音楽を受信するBluetoothの仕組みです。
|
||||||
#include "BluetoothA2DPSink.h"
|
#include "BluetoothA2DPSink.h"
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
|
@ -6,7 +13,7 @@
|
||||||
#include "SensorLogger.h"
|
#include "SensorLogger.h"
|
||||||
|
|
||||||
// スマートフォンのBluetooth一覧に表示されるスピーカー名です。
|
// スマートフォンのBluetooth一覧に表示されるスピーカー名です。
|
||||||
const char kA2dpDeviceName[] = "BT_Speaker5.6";
|
const char kA2dpDeviceName[] = "BT_Speaker5.7";
|
||||||
// I2C接続のSDA/SCLピンです。配線に合わせてここだけ変更します。
|
// I2C接続のSDA/SCLピンです。配線に合わせてここだけ変更します。
|
||||||
// 通常基板:SDA=21, SCL=22
|
// 通常基板:SDA=21, SCL=22
|
||||||
// 開発用基板:SDA=19, SCL=5
|
// 開発用基板:SDA=19, SCL=5
|
||||||
|
|
@ -210,7 +217,9 @@ void audio_state_changed_callback(esp_a2d_audio_state_t state, void *) {
|
||||||
musicPlaying = state == ESP_A2D_AUDIO_STATE_STARTED;
|
musicPlaying = state == ESP_A2D_AUDIO_STATE_STARTED;
|
||||||
Serial.printf("[INFO] Audio state changed: %d (playing=%s)\n",
|
Serial.printf("[INFO] Audio state changed: %d (playing=%s)\n",
|
||||||
state, musicPlaying ? "yes" : "no");
|
state, musicPlaying ? "yes" : "no");
|
||||||
neoPixelController.setPlaying(musicPlaying);
|
if (kNeoPixelPin >= 0) {
|
||||||
|
neoPixelController.setPlaying(musicPlaying);
|
||||||
|
}
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,33 +239,37 @@ void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Serial.println("[INFO] Firmware starting");
|
Serial.println("[INFO] Firmware starting");
|
||||||
|
|
||||||
neoPixelController.begin(18);
|
if (kNeoPixelPin >= 0) {
|
||||||
|
neoPixelController.begin(static_cast<uint8_t>(kNeoPixelPin));
|
||||||
// I2Cの配線を設定します。Wire.begin(SDA, SCL)の順です。
|
|
||||||
// OLED未接続時もSDA/SCLが浮かないようにします。
|
|
||||||
pinMode(kI2cSdaPin, INPUT_PULLUP);
|
|
||||||
pinMode(kI2cSclPin, INPUT_PULLUP);
|
|
||||||
Wire.begin(kI2cSdaPin, kI2cSclPin);
|
|
||||||
Wire.setTimeOut(50);
|
|
||||||
|
|
||||||
// OLEDが接続されているときだけ初期化と描画を行います。
|
|
||||||
uint8_t oledAddress = 0;
|
|
||||||
if (isI2cDevicePresent(0x3C)) {
|
|
||||||
oledAddress = 0x3C;
|
|
||||||
} else if (isI2cDevicePresent(0x3D)) {
|
|
||||||
oledAddress = 0x3D;
|
|
||||||
}
|
|
||||||
if (oledAddress != 0) {
|
|
||||||
u8g2.setI2CAddress(oledAddress << 1);
|
|
||||||
u8g2.begin();
|
|
||||||
u8g2.enableUTF8Print(); // UTF-8(日本語)描画を有効化
|
|
||||||
oledReady = true;
|
|
||||||
Serial.printf("[INFO] OLED detected at 0x%02X\n", oledAddress);
|
|
||||||
} else {
|
|
||||||
Serial.println("[WARNING] OLED not detected at 0x3C or 0x3D");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sensorLogger.begin(32);
|
if (kI2cConnected) {
|
||||||
|
// I2Cの配線を設定します。Wire.begin(SDA, SCL)の順です。
|
||||||
|
// OLED未接続時もSDA/SCLが浮かないようにします。
|
||||||
|
pinMode(kI2cSdaPin, INPUT_PULLUP);
|
||||||
|
pinMode(kI2cSclPin, INPUT_PULLUP);
|
||||||
|
Wire.begin(kI2cSdaPin, kI2cSclPin);
|
||||||
|
Wire.setTimeOut(50);
|
||||||
|
|
||||||
|
// OLEDが接続されているときだけ初期化と描画を行います。
|
||||||
|
uint8_t oledAddress = 0;
|
||||||
|
if (isI2cDevicePresent(0x3C)) {
|
||||||
|
oledAddress = 0x3C;
|
||||||
|
} else if (isI2cDevicePresent(0x3D)) {
|
||||||
|
oledAddress = 0x3D;
|
||||||
|
}
|
||||||
|
if (oledAddress != 0) {
|
||||||
|
u8g2.setI2CAddress(oledAddress << 1);
|
||||||
|
u8g2.begin();
|
||||||
|
u8g2.enableUTF8Print(); // UTF-8(日本語)描画を有効化
|
||||||
|
oledReady = true;
|
||||||
|
Serial.printf("[INFO] OLED detected at 0x%02X\n", oledAddress);
|
||||||
|
} else {
|
||||||
|
Serial.println("[WARNING] OLED not detected at 0x3C or 0x3D");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sensorLogger.begin(32, kI2cConnected, kBleDataConnected);
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
lastDisplayUpdate = millis();
|
lastDisplayUpdate = millis();
|
||||||
|
|
||||||
|
|
@ -284,7 +297,9 @@ void setup() {
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
// loopは何度も繰り返し実行されます。
|
// loopは何度も繰り返し実行されます。
|
||||||
neoPixelController.update();
|
if (kNeoPixelPin >= 0) {
|
||||||
|
neoPixelController.update();
|
||||||
|
}
|
||||||
bool newReading = sensorLogger.update();
|
bool newReading = sensorLogger.update();
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
if ((!bluetoothConnected || !musicPlaying)
|
if ((!bluetoothConnected || !musicPlaying)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue