/*******************************************************************************
 * PROJEKT: BLE Környezeti Érzékelő Távadó (GATT Server)
 * ESZKÖZ:  ESP32-C3 RISC-V mikrokontroller
 * SZENZOR: BME280 (I2C) - Hőmérséklet, Páratartalom, Légnyomás
 * * LEÍRÁS:
 * A program egy szabványos "Environmental Sensing" (0x181A) szolgáltatást hoz 
 * létre. Az adatfrissítés "Notify" metódussal történik, amely minimalizálja a 
 * rádióhasználatot és az energiafogyasztást. A mért lebegőpontos adatokat a 
 * BLE szabványnak megfelelő fixpontos egész számokká konvertálja az átvitel előtt.
 * * HARDVER KÖVETELMÉNYEK:
 * - ESP32-C3 fejlesztői kártya
 * - BME280 szenzor modul (SDA: GPIO 8, SCL: GPIO 9)
 * - 3.3V tápfeszültség
 * * SZOFTVER KÖVETELMÉNYEK (Arduino IDE):
 * - ESP32 Board Manager (v2.0.0+)
 * - NimBLE-Arduino könyvtár (Hatékonyabb memória-kezelés a gyári stacknél)
 * - Adafruit BME280 Library & Adafruit Unified Sensor
 * * SZABVÁNYOK:
 * - UUID 0x181A: Environmental Sensing Service
 * - UUID 0x2A6E: Temperature (századfok felbontás, int16_t)
 * - UUID 0x2A6F: Humidity (század-százalék felbontás, int16_t)
 * - UUID 0x2A6D: Pressure (tized-Pascal felbontás, uint32_t)
 ******************************************************************************/

#include <Wire.h>
#include <WiFi.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <NimBLEDevice.h>

// I2C lábkiosztás az ESP32-C3-hoz
#define I2C_SDA 8
#define I2C_SCL 9

// Bluetooth SIG szabványos 16-bites UUID-k
#define SERVICE_UUID        "181A"
#define TEMP_CHAR_UUID       "2A6E"
#define HUM_CHAR_UUID        "2A6F"
#define PRES_CHAR_UUID       "2A6D"

// Globális objektumok és pointerek a GATT eléréshez
Adafruit_BME280 bme;
NimBLEServer *pServer;
NimBLECharacteristic *pTempChar;
NimBLECharacteristic *pHumChar;
NimBLECharacteristic *pPresChar;

void setup() {
  Serial.begin(115200);
  
  // Wi-Fi kikapcsolása az interferencia csökkentése és energiamegtakarítás miatt
  WiFi.mode(WIFI_OFF);
  delay(2000);

  // I2C busz és szenzor inicializálása
  Wire.begin(I2C_SDA, I2C_SCL);
  if (!bme.begin(0x76, &Wire)) {
    Serial.println("Hiba: BME280 nem található! Ellenőrizze a bekötést.");
    while (1);
  }

  // NimBLE stack indítása és a GATT hierarchia felépítése
  NimBLEDevice::init("C3-BME280");
  pServer = NimBLEDevice::createServer();
  
  // Szolgáltatás létrehozása
  NimBLEService *pService = pServer->createService(SERVICE_UUID);

  // Jellemzők definiálása READ és NOTIFY jogosultságokkal
  pTempChar = pService->createCharacteristic(TEMP_CHAR_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
  pHumChar  = pService->createCharacteristic(HUM_CHAR_UUID,  NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
  pPresChar = pService->createCharacteristic(PRES_CHAR_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
  
  pService->start(); // Szolgáltatás aktiválása

  // Hirdetési csomag (Advertising) konfigurálása
  NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
  NimBLEAdvertisementData advData;
  
  // Flag-ek: Általános felfedezhetőség, BR/EDR (klasszikus BT) nem támogatott
  advData.setFlags(BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
  advData.addServiceUUID(NimBLEUUID(SERVICE_UUID));
  pAdvertising->setAdvertisementData(advData);

  // Válaszcsomag (Scan Response) az eszköz nevével
  NimBLEAdvertisementData scanRes;
  scanRes.setName("C3-BME280");
  pAdvertising->setScanResponseData(scanRes);
  
  pAdvertising->start(); // Hirdetés indítása az éterbe
  Serial.println("BLE Szerver üzemkész. Várakozás kliensre...");
}

void loop() {
  static unsigned long lastMillis = 0;
  
  // Adatfrissítési ciklus (3 másodpercenként)
  if (millis() - lastMillis >= 3000) {
    lastMillis = millis();

    // Szenzoradatok beolvasása és korrekciója
    float t = bme.readTemperature();
    float h = bme.readHumidity();
    float p_abs = bme.readPressure() / 100.0F; // hPa
    float p_sea = bme.seaLevelForAltitude(139.0, p_abs); // Tengerszinti nyomás kalkuláció

    if (!isnan(t)) {
      // Átalakítás szabványos fixpontos formátumra a BLE átvitelhez
      int16_t t_val = (int16_t)(t * 100);   // 22.54 -> 2254
      int16_t h_val = (int16_t)(h * 100);   // 45.20 -> 4520
      uint32_t p_val = (uint32_t)(p_sea * 10); // hPa -> tized hPa (egyszerűsített)

      // Értékek frissítése a GATT táblában
      pTempChar->setValue((uint8_t*)&t_val, 2);
      pHumChar->setValue((uint8_t*)&h_val, 2);
      pPresChar->setValue((uint8_t*)&p_val, 4);

      // Aktív kapcsolat esetén értesítés küldése (Push notification)
      if (pServer->getConnectedCount() > 0) {
        pTempChar->notify();
        pHumChar->notify();
        pPresChar->notify();
        Serial.printf("ADAT KÜLDVE -> T:%.2f °C, H:%.2f %%, P:%.1f hPa\n", t, h, p_sea);
      }
      else {
        Serial.println("STÁTUSZ: Nincs aktív kliens. Hirdetés folyamatban...");
        // Automatikus hirdetés-visszaállítás szakadás esetén
        if (!NimBLEDevice::getAdvertising()->isAdvertising()) {
          Serial.println(">>> Rekapcsolódás: Hirdetés újraindítása...");
          NimBLEDevice::startAdvertising();
        }
      }
    }
  }
}