#include <WiFi.h>
#include <esp_now.h>
#include "esp_wifi.h" // For esp_wifi_set_channel
// Float sensor pins on XIAO ESP32C6
#define FLOAT_25_PIN 16 // D6
#define FLOAT_50_PIN 1 // D1
#define FLOAT_75_PIN 2 // D2
#define FLOAT_100_PIN 21 // D3
// NEW: Battery monitoring pin
#define BATTERY_PIN 0 // A0/D0 analog pin for battery voltage measurement
//External Antenna Enable
#define WIFI_ENABLE 3
#define WIFI_ANT_CONFIG 14
#define WIFI_ANT_On_Off 1 // For External Antenna : 1 On & 0 Off
// Receiver MAC address (Main Control Unit) - Replace with your actual Main Control Unit MAC
uint8_t receiverAddress[] = {0x98, 0x88, 0xE0, 0x7f, 0xA4, 0x30};
// NEW: Enhanced data structure with battery monitoring
typedef struct {
bool level25;
bool level50;
bool level75;
bool level100;
uint8_t tankId; // 1 = Overhead Tank, 2 = Underground Tank
uint8_t batteryPercent; // NEW: Battery percentage (0-100)
float batteryVoltage; // NEW: Battery voltage for debugging
} TankData;
TankData tankData;
// NEW: Battery voltage configuration
// These values should be adjusted based on your battery type and voltage divider circuit
const float BATTERY_MAX_VOLTAGE = 4.2; // Maximum battery voltage (for Li-ion)
const float BATTERY_MIN_VOLTAGE = 3.0; // Minimum battery voltage (for Li-ion)
const float VOLTAGE_DIVIDER_RATIO = 2.84; // Adjust based on your voltage divider circuit
const float ADC_REFERENCE_VOLTAGE = 3.3; // ESP32C6 ADC reference voltage
const int ADC_RESOLUTION = 4095; // 12-bit ADC resolution
volatile int sendCallbacks = 0;
volatile bool anySendSuccess = false;
const int TOTAL_SEND_ATTEMPTS = 3;
// ESP-NOW send callback for ESP32C6/Core 3.x.x
void OnDataSent(const esp_now_send_info_t *info, esp_now_send_status_t status) {
sendCallbacks++;
// MAC address is now in info->des_addr (uint8_t[6])
Serial.print("Send status to: ");
for (int i = 0; i < 6; i++) {
Serial.printf("%02X", info->des_addr[i]);
if (i < 5) Serial.print(":");
}
Serial.print(" - ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
// Go to deep sleep after sending is confirmed
if (status == ESP_NOW_SEND_SUCCESS) {
anySendSuccess = true;
}
if (sendCallbacks >= TOTAL_SEND_ATTEMPTS) {
esp_sleep_enable_timer_wakeup(5ULL * 60ULL * 1000000ULL);
esp_deep_sleep_start();
}
// esp_sleep_enable_timer_wakeup(5 * 60 * 1000000ULL); // 5 minutes
// esp_deep_sleep_start();
}
// NEW: Function to read battery voltage and calculate percentage
void readBatteryStatus() {
// Read analog value from battery pin
int adcValue = analogRead(BATTERY_PIN);
// Convert ADC value to voltage
float voltage = (adcValue * ADC_REFERENCE_VOLTAGE) / ADC_RESOLUTION;
// Account for voltage divider if used
float batteryVoltage = voltage * VOLTAGE_DIVIDER_RATIO;
// Calculate battery percentage based on voltage curve
float batteryPercent = ((batteryVoltage - BATTERY_MIN_VOLTAGE) / (BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE)) * 100.0;
// Constrain percentage to valid range
if (batteryPercent > 100.0) batteryPercent = 100.0;
if (batteryPercent < 0.0) batteryPercent = 0.0;
// Store in tank data structure
tankData.batteryVoltage = batteryVoltage;
tankData.batteryPercent = (uint8_t)batteryPercent;
Serial.printf("OVERHEAD TANK - Battery: %.2fV, %d%%\n", batteryVoltage, tankData.batteryPercent);
}
void readFloatSwitches() {
tankData.level25 = digitalRead(FLOAT_25_PIN) == LOW;
tankData.level50 = digitalRead(FLOAT_50_PIN) == LOW;
tankData.level75 = digitalRead(FLOAT_75_PIN) == LOW;
tankData.level100 = digitalRead(FLOAT_100_PIN) == LOW;
tankData.tankId = 1; // OVERHEAD TANK IDENTIFIER
Serial.printf("OVERHEAD TANK - Float levels read - 25%%: %d, 50%%: %d, 75%%: %d, 100%%: %d\n",
tankData.level25, tankData.level50, tankData.level75, tankData.level100);
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("=== OVERHEAD TANK SENDER UNIT V5 ===");
// === External Antenna Enable ===
pinMode(WIFI_ENABLE, OUTPUT);
digitalWrite(WIFI_ENABLE, LOW); // Activate RF switch control
delay(100); // Allow switch to take effect
pinMode(WIFI_ANT_CONFIG, OUTPUT);
digitalWrite(WIFI_ANT_CONFIG, WIFI_ANT_On_Off); // Select external antenna
// ===============================
pinMode(FLOAT_25_PIN, INPUT_PULLUP);
pinMode(FLOAT_50_PIN, INPUT_PULLUP);
pinMode(FLOAT_75_PIN, INPUT_PULLUP);
pinMode(FLOAT_100_PIN, INPUT_PULLUP);
// NEW: Initialize battery monitoring pin
pinMode(BATTERY_PIN, INPUT);
WiFi.mode(WIFI_STA);
delay(100);
// Explicitly set WiFi channel for ESP-NOW on sender
esp_wifi_set_channel(11, WIFI_SECOND_CHAN_NONE);
//(not required) WiFi.setSleep(false); // keep radio fully awake during send setup
WiFi.setTxPower(WIFI_POWER_19_5dBm); // maximum Arduino Wi-Fi TX power
esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR); // enable long range mode
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW initialization failed!");
while (true) { delay(1000); }
}
Serial.println("ESP-NOW initialized.");
esp_now_register_send_cb(OnDataSent);
esp_now_peer_info_t peerInfo = {};
memcpy(peerInfo.peer_addr, receiverAddress, 6);
peerInfo.channel = 11;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add ESP-NOW peer.");
while (true) { delay(1000); }
}
Serial.println("ESP-NOW peer added.");
// Read sensor data
readFloatSwitches();
// NEW: Read battery status
readBatteryStatus();
// Send data
bool sendStarted = false;
for (int i = 0; i < 3; i++) {
esp_err_t result = esp_now_send(receiverAddress, (uint8_t*)&tankData, sizeof(tankData));
if (result == ESP_OK) {
Serial.printf("Send attempt %d initiated.\n", i + 1);
sendStarted = true;
delay(120);
} else {
Serial.printf("Send attempt %d failed to start, error: %d\n", i + 1, result);
delay(120);
}
}
if (!sendStarted) {
Serial.println("All send attempts failed to start. Going to deep sleep.");
esp_sleep_enable_timer_wakeup(5ULL * 60ULL * 1000000ULL); // 5 minutes
esp_deep_sleep_start();
}
// Deep sleep is handled in OnDataSent callback
}
void loop() {
// Not used; deep sleep handled in OnDataSent callback
}
Loading
xiao-esp32-c6
xiao-esp32-c6