BTサポート
This commit is contained in:
parent
05afe2f439
commit
3279c296a0
|
|
@ -41,6 +41,11 @@ jobs:
|
||||||
- name: Install U8g2 library
|
- name: Install U8g2 library
|
||||||
run: arduino-cli lib install "U8g2"
|
run: arduino-cli lib install "U8g2"
|
||||||
|
|
||||||
|
- name: Install sensor libraries
|
||||||
|
run: |
|
||||||
|
arduino-cli lib install "Adafruit AHTX0"
|
||||||
|
arduino-cli lib install "BH1750"
|
||||||
|
|
||||||
# コンパイル実行 (output-dir を指定して構成ファイルを確実に生成)
|
# コンパイル実行 (output-dir を指定して構成ファイルを確実に生成)
|
||||||
- name: Compile Sketch
|
- name: Compile Sketch
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
#include "SensorLogger.h"
|
||||||
|
|
||||||
|
#include <Adafruit_AHTX0.h>
|
||||||
|
#include <BH1750.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Adafruit_AHTX0 aht20;
|
||||||
|
BH1750 bh1750;
|
||||||
|
bool aht20Ready = false;
|
||||||
|
bool bh1750Ready = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SensorLogger::begin(uint8_t soilPin, const char* bluetoothName) {
|
||||||
|
soilPin_ = soilPin;
|
||||||
|
bluetoothName_ = bluetoothName;
|
||||||
|
pinMode(soilPin_, INPUT);
|
||||||
|
analogReadResolution(12);
|
||||||
|
|
||||||
|
aht20Ready = aht20.begin();
|
||||||
|
bh1750Ready = bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
|
||||||
|
|
||||||
|
bluetooth_.begin(bluetoothName);
|
||||||
|
readAndStore();
|
||||||
|
lastMeasure_ = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SensorLogger::update() {
|
||||||
|
processBluetooth();
|
||||||
|
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (now - lastMeasure_ >= kMeasureIntervalMs) {
|
||||||
|
lastMeasure_ = now;
|
||||||
|
readAndStore();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SensorReading& SensorLogger::latest() const {
|
||||||
|
return latest_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SensorLogger::hasReading() const {
|
||||||
|
return logCount_ != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SensorLogger::readAndStore() {
|
||||||
|
sensors_event_t humidityEvent;
|
||||||
|
sensors_event_t temperatureEvent;
|
||||||
|
SensorReading reading = {};
|
||||||
|
|
||||||
|
if (aht20Ready) {
|
||||||
|
aht20.getEvent(&humidityEvent, &temperatureEvent);
|
||||||
|
reading.temperature = temperatureEvent.temperature;
|
||||||
|
reading.humidity = humidityEvent.relative_humidity;
|
||||||
|
}
|
||||||
|
if (bh1750Ready) {
|
||||||
|
reading.illuminance = bh1750.readLightLevel();
|
||||||
|
}
|
||||||
|
reading.soilMoisture = analogRead(soilPin_);
|
||||||
|
reading.timestamp = currentTimestamp();
|
||||||
|
latest_ = reading;
|
||||||
|
|
||||||
|
size_t writeIndex = (logStart_ + logCount_) % kMaxLogEntries;
|
||||||
|
if (logCount_ == kMaxLogEntries) {
|
||||||
|
log_[logStart_] = reading;
|
||||||
|
logStart_ = (logStart_ + 1) % kMaxLogEntries;
|
||||||
|
} else {
|
||||||
|
log_[writeIndex] = reading;
|
||||||
|
++logCount_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SensorLogger::processBluetooth() {
|
||||||
|
while (bluetooth_.available()) {
|
||||||
|
char character = static_cast<char>(bluetooth_.read());
|
||||||
|
if (character == '\n' || character == '\r') {
|
||||||
|
if (commandBuffer_.length() != 0) {
|
||||||
|
processCommand(commandBuffer_);
|
||||||
|
commandBuffer_ = "";
|
||||||
|
}
|
||||||
|
} else if (commandBuffer_.length() < 64) {
|
||||||
|
commandBuffer_ += character;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SensorLogger::processCommand(const String& command) {
|
||||||
|
if (command == "GET_LOG") {
|
||||||
|
sendLog();
|
||||||
|
} else if (command == "CLEAR_LOG") {
|
||||||
|
clearLog();
|
||||||
|
bluetooth_.println("OK_CLEARED");
|
||||||
|
} else if (command.startsWith("TIME:")) {
|
||||||
|
unsigned long epoch = command.substring(5).toInt();
|
||||||
|
unsigned int year;
|
||||||
|
unsigned int month;
|
||||||
|
unsigned int day;
|
||||||
|
unsigned int hour;
|
||||||
|
unsigned int minute;
|
||||||
|
unsigned int second;
|
||||||
|
if (sscanf(command.c_str(), "TIME:[%u,%u,%u,%u,%u,%u]",
|
||||||
|
&year, &month, &day, &hour, &minute, &second) == 6) {
|
||||||
|
struct tm date = {};
|
||||||
|
date.tm_year = year - 1900;
|
||||||
|
date.tm_mon = month - 1;
|
||||||
|
date.tm_mday = day;
|
||||||
|
date.tm_hour = hour;
|
||||||
|
date.tm_min = minute;
|
||||||
|
date.tm_sec = second;
|
||||||
|
epoch = static_cast<unsigned long>(mktime(&date));
|
||||||
|
}
|
||||||
|
if (epoch != 0) {
|
||||||
|
epochOffset_ = epoch - millis() / 1000UL;
|
||||||
|
timeSynchronized_ = true;
|
||||||
|
bluetooth_.println("OK_TIME");
|
||||||
|
} else {
|
||||||
|
bluetooth_.println("ERROR_TIME");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SensorLogger::sendLog() {
|
||||||
|
bluetooth_.println("timestamp,temp,humid,light,soil,device");
|
||||||
|
for (size_t index = 0; index < logCount_; ++index) {
|
||||||
|
size_t logIndex = (logStart_ + index) % kMaxLogEntries;
|
||||||
|
const SensorReading& reading = log_[logIndex];
|
||||||
|
bluetooth_.printf("%lu,%.2f,%.2f,%.2f,%u,%s\n",
|
||||||
|
reading.timestamp,
|
||||||
|
reading.temperature,
|
||||||
|
reading.humidity,
|
||||||
|
reading.illuminance,
|
||||||
|
reading.soilMoisture,
|
||||||
|
bluetoothName_);
|
||||||
|
delay(20);
|
||||||
|
}
|
||||||
|
bluetooth_.println("END_LOG");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SensorLogger::clearLog() {
|
||||||
|
logStart_ = 0;
|
||||||
|
logCount_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long SensorLogger::currentTimestamp() const {
|
||||||
|
unsigned long elapsedSeconds = millis() / 1000UL;
|
||||||
|
return timeSynchronized_ ? epochOffset_ + elapsedSeconds : elapsedSeconds;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <BluetoothSerial.h>
|
||||||
|
|
||||||
|
struct SensorReading {
|
||||||
|
unsigned long timestamp;
|
||||||
|
float temperature;
|
||||||
|
float humidity;
|
||||||
|
float illuminance;
|
||||||
|
uint16_t soilMoisture;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SensorLogger {
|
||||||
|
public:
|
||||||
|
static constexpr size_t kMaxLogEntries = 2000;
|
||||||
|
|
||||||
|
void begin(uint8_t soilPin, const char* bluetoothName);
|
||||||
|
bool update();
|
||||||
|
const SensorReading& latest() const;
|
||||||
|
bool hasReading() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr unsigned long kMeasureIntervalMs = 3600000UL;
|
||||||
|
|
||||||
|
void readAndStore();
|
||||||
|
void processBluetooth();
|
||||||
|
void processCommand(const String& command);
|
||||||
|
void sendLog();
|
||||||
|
void clearLog();
|
||||||
|
unsigned long currentTimestamp() const;
|
||||||
|
|
||||||
|
BluetoothSerial bluetooth_;
|
||||||
|
SensorReading log_[kMaxLogEntries] = {};
|
||||||
|
SensorReading latest_ = {};
|
||||||
|
size_t logStart_ = 0;
|
||||||
|
size_t logCount_ = 0;
|
||||||
|
uint8_t soilPin_ = 0;
|
||||||
|
unsigned long lastMeasure_ = 0;
|
||||||
|
unsigned long epochOffset_ = 0;
|
||||||
|
bool timeSynchronized_ = false;
|
||||||
|
const char* bluetoothName_ = "";
|
||||||
|
String commandBuffer_;
|
||||||
|
};
|
||||||
|
|
@ -1,34 +1,23 @@
|
||||||
#include "BluetoothA2DPSink.h"
|
#include "BluetoothA2DPSink.h"
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#include <U8g2lib.h>
|
#include <U8g2lib.h>
|
||||||
#include <Adafruit_AHTX0.h>
|
|
||||||
#include <BH1750.h>
|
|
||||||
#include "NeoPixelController.h"
|
#include "NeoPixelController.h"
|
||||||
|
#include "SensorLogger.h"
|
||||||
|
|
||||||
const char kBluetoothName[] = "BT_Speaker5.32";
|
const char kBluetoothName[] = "BT_Speaker5.32";
|
||||||
constexpr uint8_t kSoilMoisturePin = 32;
|
|
||||||
constexpr unsigned long kSensorUpdateIntervalMs = 1000;
|
|
||||||
|
|
||||||
// I2C接続のOLED設定 (SSD1306 128x64)
|
// I2C接続のOLED設定 (SSD1306 128x64)
|
||||||
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
||||||
|
|
||||||
BluetoothA2DPSink a2dp_sink;
|
BluetoothA2DPSink a2dp_sink;
|
||||||
NeoPixelController neoPixelController;
|
NeoPixelController neoPixelController;
|
||||||
Adafruit_AHTX0 aht20;
|
SensorLogger sensorLogger;
|
||||||
BH1750 bh1750;
|
|
||||||
|
|
||||||
// 表示用変数
|
// 表示用変数
|
||||||
String currentTitle = "未接続";
|
String currentTitle = "未接続";
|
||||||
String currentArtist = "";
|
String currentArtist = "";
|
||||||
int currentVolumePercent = 0; // 音量 (0 ~ 100%)
|
int currentVolumePercent = 0; // 音量 (0 ~ 100%)
|
||||||
bool bluetoothConnected = false;
|
bool bluetoothConnected = false;
|
||||||
bool aht20Ready = false;
|
|
||||||
bool bh1750Ready = false;
|
|
||||||
unsigned long lastSensorUpdate = 0;
|
|
||||||
float temperature = 0.0f;
|
|
||||||
float humidity = 0.0f;
|
|
||||||
float illuminance = 0.0f;
|
|
||||||
int soilMoisture = 0;
|
|
||||||
|
|
||||||
// 画面全体を再描画する関数
|
// 画面全体を再描画する関数
|
||||||
void updateDisplay() {
|
void updateDisplay() {
|
||||||
|
|
@ -36,23 +25,17 @@ void updateDisplay() {
|
||||||
|
|
||||||
if (!bluetoothConnected) {
|
if (!bluetoothConnected) {
|
||||||
u8g2.setFont(u8g2_font_6x10_tf);
|
u8g2.setFont(u8g2_font_6x10_tf);
|
||||||
|
const SensorReading& reading = sensorLogger.latest();
|
||||||
|
|
||||||
char sensorLine[32];
|
char sensorLine[32];
|
||||||
if (aht20Ready) {
|
snprintf(sensorLine, sizeof(sensorLine), "T:%5.1fC H:%5.1f%%",
|
||||||
snprintf(sensorLine, sizeof(sensorLine), "T:%5.1fC H:%5.1f%%", temperature, humidity);
|
reading.temperature, reading.humidity);
|
||||||
} else {
|
|
||||||
snprintf(sensorLine, sizeof(sensorLine), "AHT20: error");
|
|
||||||
}
|
|
||||||
u8g2.drawStr(0, 12, sensorLine);
|
u8g2.drawStr(0, 12, sensorLine);
|
||||||
|
|
||||||
if (bh1750Ready) {
|
snprintf(sensorLine, sizeof(sensorLine), "Light: %6.1f lx", reading.illuminance);
|
||||||
snprintf(sensorLine, sizeof(sensorLine), "Light: %6.1f lx", illuminance);
|
|
||||||
} else {
|
|
||||||
snprintf(sensorLine, sizeof(sensorLine), "BH1750: error");
|
|
||||||
}
|
|
||||||
u8g2.drawStr(0, 30, sensorLine);
|
u8g2.drawStr(0, 30, sensorLine);
|
||||||
|
|
||||||
snprintf(sensorLine, sizeof(sensorLine), "Soil: %4d", soilMoisture);
|
snprintf(sensorLine, sizeof(sensorLine), "Soil: %4u", reading.soilMoisture);
|
||||||
u8g2.drawStr(0, 48, sensorLine);
|
u8g2.drawStr(0, 48, sensorLine);
|
||||||
u8g2.sendBuffer();
|
u8g2.sendBuffer();
|
||||||
return;
|
return;
|
||||||
|
|
@ -127,24 +110,6 @@ void connection_state_changed_callback(esp_a2d_connection_state_t state, void *)
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateSensorDisplay() {
|
|
||||||
sensors_event_t humidityEvent;
|
|
||||||
sensors_event_t temperatureEvent;
|
|
||||||
|
|
||||||
if (aht20Ready) {
|
|
||||||
aht20.getEvent(&humidityEvent, &temperatureEvent);
|
|
||||||
temperature = temperatureEvent.temperature;
|
|
||||||
humidity = humidityEvent.relative_humidity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bh1750Ready) {
|
|
||||||
illuminance = bh1750.readLightLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
soilMoisture = analogRead(kSoilMoisturePin);
|
|
||||||
updateDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
|
@ -158,12 +123,8 @@ void setup() {
|
||||||
u8g2.begin();
|
u8g2.begin();
|
||||||
u8g2.enableUTF8Print(); // UTF-8(日本語)描画を有効化
|
u8g2.enableUTF8Print(); // UTF-8(日本語)描画を有効化
|
||||||
|
|
||||||
aht20Ready = aht20.begin();
|
sensorLogger.begin(32, kBluetoothName);
|
||||||
bh1750Ready = bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
|
updateDisplay();
|
||||||
analogReadResolution(12);
|
|
||||||
|
|
||||||
updateSensorDisplay();
|
|
||||||
lastSensorUpdate = millis();
|
|
||||||
|
|
||||||
// I2S ピン設定 (BCK: 27, WS: 25, DOUT: 26)
|
// I2S ピン設定 (BCK: 27, WS: 25, DOUT: 26)
|
||||||
i2s_pin_config_t my_pin_config = {
|
i2s_pin_config_t my_pin_config = {
|
||||||
|
|
@ -187,10 +148,7 @@ void setup() {
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
neoPixelController.update();
|
neoPixelController.update();
|
||||||
|
if (sensorLogger.update() && !bluetoothConnected) {
|
||||||
unsigned long now = millis();
|
updateDisplay();
|
||||||
if (!bluetoothConnected && now - lastSensorUpdate >= kSensorUpdateIntervalMs) {
|
|
||||||
lastSensorUpdate = now;
|
|
||||||
updateSensorDisplay();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue