Updates to API and added arduino code for the XIAO-ESP32C6
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -16,7 +16,7 @@
|
|||||||
private readonly ILogger<DevicePollingService> _logger;
|
private readonly ILogger<DevicePollingService> _logger;
|
||||||
private readonly BluetoothService _bluetoothService;
|
private readonly BluetoothService _bluetoothService;
|
||||||
private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
|
||||||
private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(30);
|
private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
public DevicePollingService(
|
public DevicePollingService(
|
||||||
ILogger<DevicePollingService> logger,
|
ILogger<DevicePollingService> logger,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
8
soilProbeESP32C6/.theia/launch.json
Normal file
8
soilProbeESP32C6/.theia/launch.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
405
soilProbeESP32C6/soilProbeESP32C6.ino
Normal file
405
soilProbeESP32C6/soilProbeESP32C6.ino
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <BLEDevice.h>
|
||||||
|
#include <BLEServer.h>
|
||||||
|
#include <BLEUtils.h>
|
||||||
|
#include <BLE2902.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
#include <esp_sleep.h>
|
||||||
|
|
||||||
|
// ======== 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user