/*
 * VMC Outdoor Unit Firmware (ESP32-C6) with Robust Channel Scan & Serial DEBUG
 * 
 * --- MANUAL FLASHING PROCEDURE (If upload fails) ---
 * 1. Connect the board via USB
 * 2. Press and HOLD the "BOOT" button on the board
 * 3. Press and RELEASE the "RST" (Reset) button
 * 4. RELEASE the "BOOT" button
 * 5. Click "Upload" in Arduino IDE
 * ---------------------------------------------------
 */

#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
#include <Wire.h>
#include <SensirionI2cSht4x.h>

// --- PINS & HARDWARE CONSTANTS ---
#define VBAT_PIN 0 // Dedicated battery ADC pin for FireBeetle 2 ESP32-C6
#define SHT4X_I2C_ADDRESS 0x44

// Deep Sleep interval between readings (in seconds)
#define SLEEP_INTERVAL_SEC 300 // 5 minutes (300 seconds)

// --- MAC ADDRESS ---
// Specific MAC Address of the Indoor Unit
uint8_t indoorMacAddress[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // You have to put the MAC address of the indoor unit

// Data structure transmitted via ESP-NOW
typedef struct struct_message {
    uint32_t msgId;
    float tempExt;
    float humExt;
    float vbatExt;
} struct_message;

// ACK structure expected from indoor unit
typedef struct struct_ack {
    uint32_t msgId;
    bool received;
} struct_ack;

struct_message myData;
SensirionI2cSht4x sht4x;

volatile bool ackReceived = false;
uint32_t currentMsgId = 0;

// Callback when data is sent over the air
void OnDataSend(const uint8_t *mac_addr, esp_now_send_status_t status) {
    Serial.printf("  [ESP-NOW TX RESULT] Physical Send Status: %s\n", 
                  (status == ESP_NOW_SEND_SUCCESS) ? "DELIVERED TO MAC" : "NO ACK AT MAC LAYER");
}

// Callback when receiving ACK packet from Indoor Unit
void OnDataRecv(const esp_now_recv_info *info, const uint8_t *incomingData, int len) {
    if (len == sizeof(struct_ack)) {
        struct_ack ack;
        memcpy(&ack, incomingData, sizeof(ack));
        if (ack.msgId == currentMsgId && ack.received) {
            ackReceived = true;
            Serial.printf(">>> [ACK RECEIVED] Confirmed by Indoor Unit! ID: %lu <<<\n", ack.msgId);
        }
    }
}

// Function to read battery voltage
float readBatteryVoltage() {
    uint32_t mv = analogReadMilliVolts(VBAT_PIN);
    float voltage = (mv * 2.0f) / 1000.0f; // FireBeetle onboard 2x voltage divider
    return voltage;
}

void setup() {
    Serial.begin(115200);
    delay(1000); 
    
    Serial.println("\n==================================================");
    Serial.println("  VMC Outdoor Unit (ESP32-C6) Awake & Active     ");
    Serial.println("==================================================");

    // 1. I2C bus and SHT45 sensor initialization
    Wire.begin();
    sht4x.begin(Wire, SHT4X_I2C_ADDRESS);

    // Sensor Reading
    float temp = 0.0, hum = 0.0;
    uint16_t error = sht4x.measureHighPrecision(temp, hum);
    
    if (error) {
        Serial.printf("[SENSOR ERROR] SHT45 Read Error code: %d\n", error);
        myData.tempExt = 0.0;
        myData.humExt = 0.0;
    } else {
        myData.tempExt = temp;
        myData.humExt = hum;
        Serial.printf("[SENSOR OK] Outdoor Temp: %.2f C | Humidity: %.2f %%\n", temp, hum);
    }

    // 2. Battery voltage reading
    myData.vbatExt = readBatteryVoltage();
    Serial.printf("[BATTERY] Voltage: %.2f V\n", myData.vbatExt);

    // 3. Wi-Fi Configuration for ESP-NOW
    WiFi.mode(WIFI_STA);
    WiFi.disconnect(); // Ensure STA mode without AP association
    
    if (esp_now_init() != ESP_OK) {
        Serial.println("[ESP-NOW ERROR] Failed to initialize ESP-NOW!");
    } else {
        esp_now_register_send_cb(OnDataSend);
        esp_now_register_recv_cb(OnDataRecv);
        
        // Generate a unique message packet ID
        currentMsgId = esp_random();
        myData.msgId = currentMsgId;
        ackReceived = false;
        
        Serial.printf("[ESP-NOW ID] Generated Packet ID: %lu\n", currentMsgId);
        Serial.println("[SCAN START] Searching for Indoor Unit across Wi-Fi channels 1-13...");

        // Attempt sending on channels 1-13
        for (int channel = 1; channel <= 13 && !ackReceived; channel++) {
            Serial.printf("\n---> Testing Wi-Fi Channel %d <---\n", channel);
            
            // Set RF channel
            esp_wifi_set_promiscuous(true);
            esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
            esp_wifi_set_promiscuous(false);
            
            // Register target peer (Indoor Unit)
            esp_now_peer_info_t peerInfo = {};
            memcpy(peerInfo.peer_addr, indoorMacAddress, 6);
            peerInfo.channel = channel;
            peerInfo.encrypt = false;
            
            if (esp_now_is_peer_exist(indoorMacAddress)) {
                esp_now_del_peer(indoorMacAddress);
            }
            
            if (esp_now_add_peer(&peerInfo) == ESP_OK) {
                // Perform up to 3 fast transmission retries on this channel
                for (int attempt = 1; attempt <= 3 && !ackReceived; attempt++) {
                    Serial.printf("  Channel %d (Attempt %d/3) -> Transmitting data...\n", channel, attempt);
                    
                    esp_err_t sendResult = esp_now_send(indoorMacAddress, (uint8_t *) &myData, sizeof(myData));
                    if (sendResult != ESP_OK) {
                        Serial.printf("  [TX ERROR] esp_now_send returned code: %d\n", sendResult);
                    }
                    
                    // Wait 50ms for ACK response
                    unsigned long startWait = millis();
                    while (millis() - startWait < 50 && !ackReceived) {
                        delay(5);
                    }
                }
                
                esp_now_del_peer(indoorMacAddress);
            } else {
                Serial.printf("  [ERROR] Could not register peer on channel %d\n", channel);
            }
        }
        
        if (ackReceived) {
            Serial.println("\n[SUCCESS] Data successfully sent and acknowledged by Indoor Unit!");
        } else {
            Serial.println("\n[WARNING] All 13 channels scanned. No ACK received from Indoor Unit.");
            Serial.println("          Check if Indoor Unit is powered on and MAC match is 0B:CB:D8:00:52:54");
        }
    }

    // 4. Enter Deep Sleep
    Serial.printf("\n[DEEP SLEEP] Entering sleep for %d seconds...\n\n", SLEEP_INTERVAL_SEC);
    esp_sleep_enable_timer_wakeup((uint64_t)SLEEP_INTERVAL_SEC * 1000000ULL);
    esp_deep_sleep_start();
}

void loop() {
    // Execution resumes in setup() after Deep Sleep wake-up
}