From 5e052dba930be6288f38af2af03e155b093bb5e4 Mon Sep 17 00:00:00 2001 From: Koichi Ito Date: Sun, 20 Sep 2026 17:24:08 +0900 Subject: [PATCH] =?UTF-8?q?RTC=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firmware/SensorLogger.cpp | 53 ++++++++++++++++++++++++++++++++++++--- firmware/SensorLogger.h | 8 +++--- firmware/firmware.ino | 40 ++++++++++++++++++++++------- 3 files changed, 86 insertions(+), 15 deletions(-) diff --git a/firmware/SensorLogger.cpp b/firmware/SensorLogger.cpp index 8bd63bd..90a7061 100644 --- a/firmware/SensorLogger.cpp +++ b/firmware/SensorLogger.cpp @@ -12,11 +12,17 @@ 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"; +constexpr int64_t kInitialYear = 1700; +constexpr int64_t kSecondsPerDay = 86400; Adafruit_AHTX0 aht20; BH1750 bh1750; bool aht20Ready = false; bool bh1750Ready = false; + +bool isLeapYear(int year) { + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +} } class SensorLoggerCallbacks : public BLECharacteristicCallbacks { @@ -96,6 +102,47 @@ bool SensorLogger::hasReading() const { return logCount_ != 0; } +size_t SensorLogger::logCount() const { + return logCount_; +} + +bool SensorLogger::getCurrentTime(char* buffer, size_t bufferSize) const { + if (bufferSize == 0) { + return false; + } + + int64_t timestamp = currentTimestamp(); + if (timeSynchronized_) { + time_t unixTimestamp = static_cast(timestamp); + struct tm date; + localtime_r(&unixTimestamp, &date); + strftime(buffer, bufferSize, "%Y/%m/%d %H:%M:%S", &date); + return true; + } + + int year = static_cast(kInitialYear); + int month = 1; + int day = 1; + int64_t days = timestamp / kSecondsPerDay; + int seconds = static_cast(timestamp % kSecondsPerDay); + while (days >= (isLeapYear(year) ? 366 : 365)) { + days -= isLeapYear(year) ? 366 : 365; + ++year; + } + const int daysInMonth[] = { + 31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + while (days >= daysInMonth[month - 1]) { + days -= daysInMonth[month - 1]; + ++month; + } + day += static_cast(days); + snprintf(buffer, bufferSize, "%04d/%02d/%02d %02d:%02d:%02d", + year, month, day, seconds / 3600, (seconds / 60) % 60, seconds % 60); + return true; +} + void SensorLogger::readAndStore() { sensors_event_t humidityEvent; sensors_event_t temperatureEvent; @@ -193,7 +240,7 @@ void SensorLogger::sendLog() { size_t logIndex = (logStart_ + index) % kMaxLogEntries; const SensorReading& reading = log_[logIndex]; char line[128]; - snprintf(line, sizeof(line), "%lu,%.2f,%.2f,%.2f,%u,%s\n", + snprintf(line, sizeof(line), "%lld,%.2f,%.2f,%.2f,%u,%s\n", reading.timestamp, reading.temperature, reading.humidity, @@ -224,7 +271,7 @@ void SensorLogger::clearLog() { logCount_ = 0; } -unsigned long SensorLogger::currentTimestamp() const { - unsigned long elapsedSeconds = millis() / 1000UL; +int64_t SensorLogger::currentTimestamp() const { + int64_t elapsedSeconds = millis() / 1000UL; return timeSynchronized_ ? epochOffset_ + elapsedSeconds : elapsedSeconds; } diff --git a/firmware/SensorLogger.h b/firmware/SensorLogger.h index 0ad9f42..adfe885 100644 --- a/firmware/SensorLogger.h +++ b/firmware/SensorLogger.h @@ -6,7 +6,7 @@ class BLECharacteristic; class BLEServer; struct SensorReading { - unsigned long timestamp; + int64_t timestamp; float temperature; float humidity; float illuminance; @@ -21,6 +21,8 @@ public: bool update(); const SensorReading& latest() const; bool hasReading() const; + size_t logCount() const; + bool getCurrentTime(char* buffer, size_t bufferSize) const; private: static constexpr unsigned long kMeasureIntervalMs = 3600000UL; @@ -32,7 +34,7 @@ private: void sendNotification(const char* data); void sendLog(); void clearLog(); - unsigned long currentTimestamp() const; + int64_t currentTimestamp() const; BLEServer* server_ = nullptr; BLECharacteristic* txCharacteristic_ = nullptr; @@ -42,7 +44,7 @@ private: size_t logCount_ = 0; uint8_t soilPin_ = 0; unsigned long lastMeasure_ = 0; - unsigned long epochOffset_ = 0; + int64_t epochOffset_ = 0; bool timeSynchronized_ = false; String bluetoothName_; String commandBuffer_; diff --git a/firmware/firmware.ino b/firmware/firmware.ino index 8754b3d..177b172 100644 --- a/firmware/firmware.ino +++ b/firmware/firmware.ino @@ -4,7 +4,7 @@ #include "NeoPixelController.h" #include "SensorLogger.h" -const char kBluetoothName[] = "BT_Speaker5.32"; +const char kBluetoothName[] = "BT_Speaker5.4"; // I2C接続のOLED設定 (SSD1306 128x64) U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); @@ -18,25 +18,37 @@ String currentTitle = "未接続"; String currentArtist = ""; int currentVolumePercent = 0; // 音量 (0 ~ 100%) bool bluetoothConnected = false; +bool musicPlaying = false; +unsigned long lastDisplayUpdate = 0; // 画面全体を再描画する関数 void updateDisplay() { u8g2.clearBuffer(); - if (!bluetoothConnected) { + if (!bluetoothConnected || !musicPlaying) { u8g2.setFont(u8g2_font_6x10_tf); const SensorReading& reading = sensorLogger.latest(); char sensorLine[32]; + char timeLine[24]; + if (sensorLogger.getCurrentTime(timeLine, sizeof(timeLine))) { + u8g2.drawStr(0, 9, timeLine); + } else { + u8g2.drawStr(0, 9, "RTC: --/--/-- --:--:--"); + } + + snprintf(sensorLine, sizeof(sensorLine), "Log: %u", static_cast(sensorLogger.logCount())); + u8g2.drawStr(0, 20, sensorLine); + snprintf(sensorLine, sizeof(sensorLine), "T:%5.1fC H:%5.1f%%", reading.temperature, reading.humidity); - u8g2.drawStr(0, 12, sensorLine); + u8g2.drawStr(0, 32, sensorLine); snprintf(sensorLine, sizeof(sensorLine), "Light: %6.1f lx", reading.illuminance); - u8g2.drawStr(0, 30, sensorLine); + u8g2.drawStr(0, 44, sensorLine); snprintf(sensorLine, sizeof(sensorLine), "Soil: %4u", reading.soilMoisture); - u8g2.drawStr(0, 48, sensorLine); + u8g2.drawStr(0, 56, sensorLine); u8g2.sendBuffer(); return; } @@ -88,7 +100,7 @@ void avrc_metadata_callback(uint8_t id, const uint8_t *text) { updated = true; } - if (updated && bluetoothConnected) { + if (updated && bluetoothConnected && musicPlaying) { updateDisplay(); } } @@ -96,17 +108,22 @@ void avrc_metadata_callback(uint8_t id, const uint8_t *text) { // 音量変更時のコールバック関数 void volume_changed_callback(int volume) { currentVolumePercent = map(volume, 0, 127, 0, 100); - if (bluetoothConnected) { + if (bluetoothConnected && musicPlaying) { updateDisplay(); } } void audio_state_changed_callback(esp_a2d_audio_state_t state, void *) { - neoPixelController.setPlaying(state == ESP_A2D_AUDIO_STATE_STARTED); + musicPlaying = state == ESP_A2D_AUDIO_STATE_STARTED; + neoPixelController.setPlaying(musicPlaying); + updateDisplay(); } void connection_state_changed_callback(esp_a2d_connection_state_t state, void *) { bluetoothConnected = state == ESP_A2D_CONNECTION_STATE_CONNECTED; + if (!bluetoothConnected) { + musicPlaying = false; + } updateDisplay(); } @@ -125,6 +142,7 @@ void setup() { sensorLogger.begin(32); updateDisplay(); + lastDisplayUpdate = millis(); // I2S ピン設定 (BCK: 27, WS: 25, DOUT: 26) i2s_pin_config_t my_pin_config = { @@ -148,7 +166,11 @@ void setup() { void loop() { neoPixelController.update(); - if (sensorLogger.update() && !bluetoothConnected) { + bool newReading = sensorLogger.update(); + unsigned long now = millis(); + if ((!bluetoothConnected || !musicPlaying) + && (newReading || now - lastDisplayUpdate >= 1000UL)) { + lastDisplayUpdate = now; updateDisplay(); } } \ No newline at end of file