EnvLogサポート

This commit is contained in:
Koichi Ito 2026-09-20 16:34:30 +09:00
parent 3279c296a0
commit 68a5f0dcce
3 changed files with 109 additions and 21 deletions

View File

@ -2,25 +2,76 @@
#include <Adafruit_AHTX0.h> #include <Adafruit_AHTX0.h>
#include <BH1750.h> #include <BH1750.h>
#include <BLE2902.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <time.h> #include <time.h>
namespace { namespace {
constexpr char kNusServiceUuid[] = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
constexpr char kNusRxUuid[] = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
constexpr char kNusTxUuid[] = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
Adafruit_AHTX0 aht20; Adafruit_AHTX0 aht20;
BH1750 bh1750; BH1750 bh1750;
bool aht20Ready = false; bool aht20Ready = false;
bool bh1750Ready = false; bool bh1750Ready = false;
} }
void SensorLogger::begin(uint8_t soilPin, const char* bluetoothName) { class SensorLoggerCallbacks : public BLECharacteristicCallbacks {
public:
explicit SensorLoggerCallbacks(SensorLogger* logger) : logger_(logger) {}
void onWrite(BLECharacteristic* characteristic) override {
logger_->receiveBluetoothData(characteristic->getValue().c_str());
}
private:
SensorLogger* logger_;
};
class SensorLoggerServerCallbacks : public BLEServerCallbacks {
public:
void onConnect(BLEServer*) override {}
void onDisconnect(BLEServer* server) override {
server->startAdvertising();
}
};
void SensorLogger::begin(uint8_t soilPin) {
soilPin_ = soilPin; soilPin_ = soilPin;
bluetoothName_ = bluetoothName;
pinMode(soilPin_, INPUT); pinMode(soilPin_, INPUT);
analogReadResolution(12); analogReadResolution(12);
aht20Ready = aht20.begin(); aht20Ready = aht20.begin();
bh1750Ready = bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); bh1750Ready = bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
bluetooth_.begin(bluetoothName); uint64_t mac = ESP.getEfuseMac();
char name[14];
snprintf(name, sizeof(name), "EnvLog-%05llX", mac & 0xFFFFF);
bluetoothName_ = name;
BLEDevice::init(bluetoothName_.c_str());
server_ = BLEDevice::createServer();
server_->setCallbacks(new SensorLoggerServerCallbacks());
BLEService* service = server_->createService(kNusServiceUuid);
BLECharacteristic* rxCharacteristic = service->createCharacteristic(
kNusRxUuid, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
rxCharacteristic->setCallbacks(new SensorLoggerCallbacks(this));
txCharacteristic_ = service->createCharacteristic(
kNusTxUuid, BLECharacteristic::PROPERTY_NOTIFY);
txCharacteristic_->addDescriptor(new BLE2902());
service->start();
BLEAdvertising* advertising = BLEDevice::getAdvertising();
advertising->addServiceUUID(kNusServiceUuid);
advertising->setScanResponse(true);
advertising->setMinPreferred(0x06);
advertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
readAndStore(); readAndStore();
lastMeasure_ = millis(); lastMeasure_ = millis();
} }
@ -73,13 +124,25 @@ void SensorLogger::readAndStore() {
} }
void SensorLogger::processBluetooth() { void SensorLogger::processBluetooth() {
while (bluetooth_.available()) { while (commandBuffer_.indexOf('\n') >= 0 || commandBuffer_.indexOf('\r') >= 0) {
char character = static_cast<char>(bluetooth_.read()); int lineEnd = commandBuffer_.indexOf('\n');
if (character == '\n' || character == '\r') { int carriageReturn = commandBuffer_.indexOf('\r');
if (commandBuffer_.length() != 0) { if (lineEnd < 0 || (carriageReturn >= 0 && carriageReturn < lineEnd)) {
processCommand(commandBuffer_); lineEnd = carriageReturn;
commandBuffer_ = "";
} }
String command = commandBuffer_.substring(0, lineEnd);
commandBuffer_.remove(0, lineEnd + 1);
if (command.length() != 0) {
processCommand(command);
}
}
}
void SensorLogger::receiveBluetoothData(const String& data) {
for (size_t index = 0; index < data.length(); ++index) {
char character = data[index];
if (character == '\n' || character == '\r') {
commandBuffer_ += character;
} else if (commandBuffer_.length() < 64) { } else if (commandBuffer_.length() < 64) {
commandBuffer_ += character; commandBuffer_ += character;
} }
@ -91,7 +154,8 @@ void SensorLogger::processCommand(const String& command) {
sendLog(); sendLog();
} else if (command == "CLEAR_LOG") { } else if (command == "CLEAR_LOG") {
clearLog(); clearLog();
bluetooth_.println("OK_CLEARED"); txCharacteristic_->setValue("OK_CLEARED\n");
txCharacteristic_->notify();
} else if (command.startsWith("TIME:")) { } else if (command.startsWith("TIME:")) {
unsigned long epoch = command.substring(5).toInt(); unsigned long epoch = command.substring(5).toInt();
unsigned int year; unsigned int year;
@ -114,28 +178,45 @@ void SensorLogger::processCommand(const String& command) {
if (epoch != 0) { if (epoch != 0) {
epochOffset_ = epoch - millis() / 1000UL; epochOffset_ = epoch - millis() / 1000UL;
timeSynchronized_ = true; timeSynchronized_ = true;
bluetooth_.println("OK_TIME"); txCharacteristic_->setValue("OK_TIME\n");
txCharacteristic_->notify();
} else { } else {
bluetooth_.println("ERROR_TIME"); txCharacteristic_->setValue("ERROR_TIME\n");
txCharacteristic_->notify();
} }
} }
} }
void SensorLogger::sendLog() { void SensorLogger::sendLog() {
bluetooth_.println("timestamp,temp,humid,light,soil,device"); sendNotification("timestamp,temp,humid,light,soil,device\n");
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;
const SensorReading& reading = log_[logIndex]; const SensorReading& reading = log_[logIndex];
bluetooth_.printf("%lu,%.2f,%.2f,%.2f,%u,%s\n", char line[128];
snprintf(line, sizeof(line), "%lu,%.2f,%.2f,%.2f,%u,%s\n",
reading.timestamp, reading.timestamp,
reading.temperature, reading.temperature,
reading.humidity, reading.humidity,
reading.illuminance, reading.illuminance,
reading.soilMoisture, reading.soilMoisture,
bluetoothName_); bluetoothName_.c_str());
sendNotification(line);
delay(20); delay(20);
} }
bluetooth_.println("END_LOG"); sendNotification("END_LOG\n");
}
void SensorLogger::sendNotification(const char* data) {
constexpr size_t kNotificationSize = 20;
uint8_t notification[kNotificationSize];
size_t length = strlen(data);
for (size_t offset = 0; offset < length; offset += kNotificationSize) {
size_t chunkSize = min(kNotificationSize, length - offset);
memcpy(notification, data + offset, chunkSize);
txCharacteristic_->setValue(notification, chunkSize);
txCharacteristic_->notify();
delay(10);
}
} }
void SensorLogger::clearLog() { void SensorLogger::clearLog() {

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include <BluetoothSerial.h>
class BLECharacteristic;
class BLEServer;
struct SensorReading { struct SensorReading {
unsigned long timestamp; unsigned long timestamp;
@ -15,7 +17,7 @@ class SensorLogger {
public: public:
static constexpr size_t kMaxLogEntries = 2000; static constexpr size_t kMaxLogEntries = 2000;
void begin(uint8_t soilPin, const char* bluetoothName); void begin(uint8_t soilPin);
bool update(); bool update();
const SensorReading& latest() const; const SensorReading& latest() const;
bool hasReading() const; bool hasReading() const;
@ -26,11 +28,14 @@ private:
void readAndStore(); void readAndStore();
void processBluetooth(); void processBluetooth();
void processCommand(const String& command); void processCommand(const String& command);
void receiveBluetoothData(const String& data);
void sendNotification(const char* data);
void sendLog(); void sendLog();
void clearLog(); void clearLog();
unsigned long currentTimestamp() const; unsigned long currentTimestamp() const;
BluetoothSerial bluetooth_; BLEServer* server_ = nullptr;
BLECharacteristic* txCharacteristic_ = nullptr;
SensorReading log_[kMaxLogEntries] = {}; SensorReading log_[kMaxLogEntries] = {};
SensorReading latest_ = {}; SensorReading latest_ = {};
size_t logStart_ = 0; size_t logStart_ = 0;
@ -39,6 +44,8 @@ private:
unsigned long lastMeasure_ = 0; unsigned long lastMeasure_ = 0;
unsigned long epochOffset_ = 0; unsigned long epochOffset_ = 0;
bool timeSynchronized_ = false; bool timeSynchronized_ = false;
const char* bluetoothName_ = ""; String bluetoothName_;
String commandBuffer_; String commandBuffer_;
friend class SensorLoggerCallbacks;
}; };

View File

@ -123,7 +123,7 @@ void setup() {
u8g2.begin(); u8g2.begin();
u8g2.enableUTF8Print(); // UTF-8(日本語)描画を有効化 u8g2.enableUTF8Print(); // UTF-8(日本語)描画を有効化
sensorLogger.begin(32, kBluetoothName); sensorLogger.begin(32);
updateDisplay(); updateDisplay();
// I2S ピン設定 (BCK: 27, WS: 25, DOUT: 26) // I2S ピン設定 (BCK: 27, WS: 25, DOUT: 26)