diff --git a/house-plant-api/.vs/house-plant-api/DesignTimeBuild/.dtbcache.v2 b/house-plant-api/.vs/house-plant-api/DesignTimeBuild/.dtbcache.v2 index 806b254..ca982bc 100644 Binary files a/house-plant-api/.vs/house-plant-api/DesignTimeBuild/.dtbcache.v2 and b/house-plant-api/.vs/house-plant-api/DesignTimeBuild/.dtbcache.v2 differ diff --git a/house-plant-api/.vs/house-plant-api/v17/.suo b/house-plant-api/.vs/house-plant-api/v17/.suo index f9683ca..f6585ae 100644 Binary files a/house-plant-api/.vs/house-plant-api/v17/.suo and b/house-plant-api/.vs/house-plant-api/v17/.suo differ diff --git a/house-plant-api/Services/DevicePollingService.cs b/house-plant-api/Services/DevicePollingService.cs index 47d4e65..ff5817c 100644 --- a/house-plant-api/Services/DevicePollingService.cs +++ b/house-plant-api/Services/DevicePollingService.cs @@ -16,7 +16,7 @@ private readonly ILogger _logger; private readonly BluetoothService _bluetoothService; private readonly IDbContextFactory _dbContextFactory; - private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(30); + private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(10); public DevicePollingService( ILogger logger, diff --git a/house-plant-api/devices.db b/house-plant-api/devices.db index 2945bcd..0de02ec 100644 Binary files a/house-plant-api/devices.db and b/house-plant-api/devices.db differ diff --git a/house-plant-api/devices.db-shm b/house-plant-api/devices.db-shm index fe9ac28..4710d5b 100644 Binary files a/house-plant-api/devices.db-shm and b/house-plant-api/devices.db-shm differ diff --git a/house-plant-api/devices.db-wal b/house-plant-api/devices.db-wal index e69de29..9e352c4 100644 Binary files a/house-plant-api/devices.db-wal and b/house-plant-api/devices.db-wal differ diff --git a/soilProbeESP32C6/.theia/launch.json b/soilProbeESP32C6/.theia/launch.json new file mode 100644 index 0000000..7e4253b --- /dev/null +++ b/soilProbeESP32C6/.theia/launch.json @@ -0,0 +1,8 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + "version": "0.2.0", + "configurations": [ + + ] +} diff --git a/soilProbeESP32C6/soilProbeESP32C6.ino b/soilProbeESP32C6/soilProbeESP32C6.ino new file mode 100644 index 0000000..911a1e8 --- /dev/null +++ b/soilProbeESP32C6/soilProbeESP32C6.ino @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include +#include +#include + +// ======== BLE UUIDs ======== +#define SERVICE_UUID "12345678-1234-1234-1234-123456789abc" +#define CHARACTERISTIC_UUID "abcd1234-5678-1234-5678-abcdef123456" + +// ======== Hardware & ADC ======== +#define MOISTURE_PIN A0 +#define LED_PIN 2 // Example LED pin (built-in LED on many ESP32 boards) +#define BUTTON_PIN D4 // GPIO 4 for calibration button (recommended over GPIO 0) + +// ======== Constants ======== +const int DRY_VALUE_DEFAULT = 500; +const int WET_VALUE_DEFAULT = 200; +const int ADC_RESOLUTION = 10; // 10-bit ADC (0–1023) + +// ======== Reading & Averaging ======== +const int NUM_SAMPLES = 5; // Number of samples to average +const int SAMPLE_DELAY_MS = 50; // Delay between each sample (ms) + +// ======== Connection Timeout Settings ======== +const unsigned long CONNECTION_TIMEOUT_MS = 120000; // 2 minutes to establish connection +unsigned long connectionStartTime = 0; +bool connectionTimerStarted = false; + +// ======== Sleep Settings ======== +const uint64_t DEEP_SLEEP_DURATION_US = 60ULL * 60ULL * 1000000ULL; // 1 hour in microseconds + +// ======== Calibration Variables ======== +int dryValue = DRY_VALUE_DEFAULT; // Initialize with default dry value +int wetValue = WET_VALUE_DEFAULT; // Initialize with default wet value + +// ======== Device Configuration ======== +const char* deviceName = "003-SoilSensor"; // Default device name + +// ======== EEPROM Addresses ======== +const int EEPROM_SIZE = 512; +const int EEPROM_DRY_ADDRESS = 0; // Bytes 0-3 +const int EEPROM_WET_ADDRESS = 4; // Bytes 4-7 + +// ======== BLE Objects ======== +BLEServer* pServer = nullptr; +BLECharacteristic* pCharacteristic = nullptr; + +// ======== Device State Flags ======== +bool isConnected = false; +bool dataSent = false; + +// ======== Calibration Variables ======== +enum DeviceState { + STATE_NORMAL, + STATE_CALIBRATE_NOT_IN_SOIL, + STATE_CALIBRATE_IN_SOIL +}; + +DeviceState currentState = STATE_NORMAL; +int calibrationCount = 0; + +// ======== Button Handling ======== +const unsigned long DEBOUNCE_DELAY = 50; // 50ms debounce delay +bool lastButtonState = HIGH; // Assuming pull-up resistor +bool buttonPressed = false; +unsigned long lastDebounceTime = 0; + +// ======== LED Blinking Parameters ======== +unsigned long previousBlinkTime = 0; +const unsigned long BLINK_ON_DURATION = 200; // 200ms ON +const unsigned long BLINK_OFF_DURATION = 100; // 100ms OFF +bool ledState = false; + +// ======== Function Prototypes ======== +int readSoilMoisture(); +int getAveragedMoisture(int numSamples); +void enterDeepSleep(); +void handleCalibrationButtonPress(); +void readCalibrationValues(); +void writeCalibrationValues(int dry, int wet); +void checkButtonPress(); +void updateLED(); + +// ======== Custom Server Callbacks ======== +class MyServerCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer* pServer) override { + Serial.println("Client connected."); + isConnected = true; + + // Turn on LED to indicate active state + digitalWrite(LED_PIN, HIGH); + + // Stop connection timeout timer + connectionTimerStarted = false; + connectionStartTime = 0; + } + + void onDisconnect(BLEServer* pServer) override { + Serial.println("Client disconnected."); + isConnected = false; + dataSent = false; + + // Restart advertising to allow new connections + pServer->startAdvertising(); + + // Restart connection timeout timer + connectionTimerStarted = true; + connectionStartTime = millis(); + + // Turn off LED to indicate sleep state + digitalWrite(LED_PIN, LOW); + } +}; + +// ======== Sensor Reading Functions ======== +/** + * Perform a single soil moisture reading (0–100%). + */ +int readSoilMoisture() { + int rawValue = analogRead(MOISTURE_PIN); + int percent = map(rawValue, dryValue, wetValue, 0, 100); + return constrain(percent, 0, 100); +} + +/** + * Gathers multiple samples, applies a small delay between each, + * averages them, and returns the final moisture value. + */ +int getAveragedMoisture(int numSamples) { + long sum = 0; + + for (int i = 0; i < numSamples; i++) { + sum += readSoilMoisture(); + delay(SAMPLE_DELAY_MS); // Small delay between samples + } + + int avg = (int)(sum / numSamples); + return avg; +} + +/** + * Enter deep sleep mode for the defined duration. + */ +void enterDeepSleep() { + Serial.println("Entering deep sleep for 1 hour..."); + + // Turn off LED to indicate sleep state + digitalWrite(LED_PIN, LOW); + + // Disable BLE before sleeping + BLEDevice::deinit(true); + + // Configure deep sleep wake-up time + esp_sleep_enable_timer_wakeup(DEEP_SLEEP_DURATION_US); + + // Enter deep sleep + esp_deep_sleep_start(); +} + +/** + * Handle calibration button presses to manage calibration steps. + */ +void handleCalibrationButtonPress() { + calibrationCount++; + + switch (calibrationCount) { + case 1: + // Calibration Step 1: Record Dry Value + Serial.println("Calibration Step 1: Place device in dry soil and press button."); + currentState = STATE_CALIBRATE_NOT_IN_SOIL; + break; + + case 2: + // Calibration Step 2: Record Wet Value + Serial.println("Calibration Step 2: Place device in wet soil and press button."); + currentState = STATE_CALIBRATE_IN_SOIL; + break; + + case 3: + // Calibration Complete + Serial.println("Calibration Complete. Returning to normal operation."); + currentState = STATE_NORMAL; + calibrationCount = 0; // Reset for next calibration + break; + + default: + // Reset Calibration + Serial.println("Calibration Reset. Returning to normal operation."); + currentState = STATE_NORMAL; + calibrationCount = 0; + break; + } + + updateLED(); +} + +/** + * Read calibration values from EEPROM. + */ +void readCalibrationValues() { + EEPROM.get(EEPROM_DRY_ADDRESS, dryValue); + EEPROM.get(EEPROM_WET_ADDRESS, wetValue); + + // Validate calibration values + if (dryValue == 0xFFFFFFFF || wetValue == 0xFFFFFFFF) { + dryValue = DRY_VALUE_DEFAULT; + wetValue = WET_VALUE_DEFAULT; + } +} + +/** + * Write calibration values to EEPROM. + */ +void writeCalibrationValues(int dry, int wet) { + EEPROM.put(EEPROM_DRY_ADDRESS, dry); + EEPROM.put(EEPROM_WET_ADDRESS, wet); + EEPROM.commit(); +} + +/** + * Check for button presses with debouncing. + */ +void checkButtonPress() { + bool reading = digitalRead(BUTTON_PIN); + + if (reading != lastButtonState) { + lastDebounceTime = millis(); + } + + if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) { + if (reading == LOW && lastButtonState == HIGH) { + buttonPressed = true; + } + } + + lastButtonState = reading; +} + +/** + * Update LED based on the current state. + */ +void updateLED() { + // Reset LED parameters + previousBlinkTime = millis(); + ledState = false; + + switch (currentState) { + case STATE_NORMAL: + // LED is handled by BLE connection callbacks + digitalWrite(LED_PIN, LOW); // Ensure LED is off in normal mode + break; + + case STATE_CALIBRATE_NOT_IN_SOIL: + // Solid LED to indicate calibration for dry soil + digitalWrite(LED_PIN, HIGH); + break; + + case STATE_CALIBRATE_IN_SOIL: + // Start blinking in 2:1 ratio for wet soil calibration + digitalWrite(LED_PIN, HIGH); // Start with LED ON + ledState = true; + break; + } +} + +/** + * Initialize EEPROM and read calibration values. + */ +void initializeEEPROM() { + if (!EEPROM.begin(EEPROM_SIZE)) { + Serial.println("Failed to initialize EEPROM"); + // Handle EEPROM initialization failure if necessary + } + readCalibrationValues(); +} + +/** + * Update mapping based on calibrated dry and wet values. + */ +int mapMoisture(int rawValue) { + int percent = map(rawValue, dryValue, wetValue, 0, 100); + return constrain(percent, 0, 100); +} + +// ======== Setup Function ======== +void setup() { + // Initialize Serial + Serial.begin(115200); + delay(1000); // Allow time for serial to initialize + + // Initialize LED pin + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LOW); // LED off initially + + // Initialize Button pin + pinMode(BUTTON_PIN, INPUT_PULLUP); // Use INPUT_PULLUP if using internal resistor + // If using external resistor, use INPUT instead: + // pinMode(BUTTON_PIN, INPUT); + + // Initialize EEPROM + initializeEEPROM(); + Serial.printf("Calibrated Dry Value: %d\n", dryValue); + Serial.printf("Calibrated Wet Value: %d\n", wetValue); + + // Initialize BLE with deviceName variable + BLEDevice::init(deviceName); // Initialize BLE with the variable device name + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create BLE service + BLEService* pService = pServer->createService(SERVICE_UUID); + + // Create BLE characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY + ); + + // Add descriptor for notifications + pCharacteristic->addDescriptor(new BLE2902()); + + // Start the service + pService->start(); + + // Start advertising + pServer->getAdvertising()->addServiceUUID(SERVICE_UUID); + pServer->getAdvertising()->setScanResponse(true); + pServer->getAdvertising()->start(); + + Serial.println("BLE server is running..."); + + // Start connection timeout timer + connectionTimerStarted = true; + connectionStartTime = millis(); + + // Initialize state and LED + currentState = STATE_NORMAL; + calibrationCount = 0; + updateLED(); +} + +// ======== Loop Function ======== +void loop() { + // Check for button presses + checkButtonPress(); + + if (buttonPressed) { + buttonPressed = false; + if (currentState != STATE_NORMAL) { + // Handle calibration step + handleCalibrationButtonPress(); + } else { + // Initiate calibration + Serial.println("Button pressed. Initiating calibration mode."); + currentState = STATE_CALIBRATE_NOT_IN_SOIL; + calibrationCount = 1; + updateLED(); + } + } + + unsigned long currentTime = millis(); + + // ======== Connection Timeout Logic ======== + if (connectionTimerStarted) { + if (currentTime - connectionStartTime >= CONNECTION_TIMEOUT_MS) { + Serial.println("No connection within 2 minutes. Entering deep sleep."); + enterDeepSleep(); + } + } + + // ======== BLE Connected Logic ======== + if (isConnected && !dataSent && currentState == STATE_NORMAL) { + // Take sensor readings + int rawMoisture = analogRead(MOISTURE_PIN); + int moistureAvg = mapMoisture(rawMoisture); + Serial.printf("Averaged Soil Moisture: %d%%\n", moistureAvg); + + // Update the characteristic and notify the client + pCharacteristic->setValue(moistureAvg); + pCharacteristic->notify(); + + dataSent = true; + + // Immediately enter deep sleep after sending data + enterDeepSleep(); + } + + // ======== LED Update ======== + updateLED(); + + // ======== LED Blinking Logic ======== + if (currentState == STATE_CALIBRATE_IN_SOIL) { + if (millis() - previousBlinkTime >= (ledState ? BLINK_ON_DURATION : BLINK_OFF_DURATION)) { + ledState = !ledState; + digitalWrite(LED_PIN, ledState ? HIGH : LOW); + previousBlinkTime = millis(); + } + } + + // Short delay to prevent watchdog resets + delay(100); +}