diff --git a/ArduinoUnoDo/WaterMonitor/CalibrationService.cpp b/ArduinoUnoDo/WaterMonitor/CalibrationService.cpp new file mode 100644 index 0000000..121c9b0 --- /dev/null +++ b/ArduinoUnoDo/WaterMonitor/CalibrationService.cpp @@ -0,0 +1,175 @@ +#include "CalibrationService.h" +#include + +CalibrationService::CalibrationService(GravitySensorHub* sensorHub, SdService* sdService) : + _sensorHub(sensorHub), + _sdService(sdService), + _isCalibrating(false), + _currentCalibrationType(CALIBRATION_PH_3POINT), + _calibrationStartTime(0) { +} + +void CalibrationService::setup() { + loadCalibrationData(); + applyCalibration(); +} + +void CalibrationService::loop() { + if (!_isCalibrating) return; + + unsigned long currentTime = millis(); + if (currentTime - _calibrationStartTime < 10000) return; // Wait 10 seconds for stability + + switch (_currentCalibrationType) { + case CALIBRATION_PH_3POINT: + calibratePh3Point(); + break; + case CALIBRATION_PH_2POINT: + calibratePh2Point(); + break; + case CALIBRATION_EC_1POINT: + calibrateEc1Point(); + break; + case CALIBRATION_DO_2POINT: + calibrateDo2Point(); + break; + case CALIBRATION_ORP_1POINT: + calibrateOrp1Point(); + break; + } + + _isCalibrating = false; + saveCalibrationData(); + Serial.println(F("Calibration completed!")); +} + +void CalibrationService::startCalibration(CalibrationType type) { + _currentCalibrationType = type; + _isCalibrating = true; + _calibrationStartTime = millis(); + printCalibrationInstructions(); +} + +void CalibrationService::stopCalibration() { + _isCalibrating = false; +} + +bool CalibrationService::isCalibrating() { + return _isCalibrating; +} + +void CalibrationService::setPhCalibration(float ph7, float ph4, float ph10) { + _calibrationData.ph7Calibration = ph7; + _calibrationData.ph4Calibration = ph4; + _calibrationData.ph10Calibration = ph10; + _calibrationData.isCalibrated[0] = true; + applyCalibration(); +} + +void CalibrationService::setEcCalibration(float ecValue) { + _calibrationData.ecCalibration = ecValue; + _calibrationData.isCalibrated[3] = true; + applyCalibration(); +} + +void CalibrationService::setDoCalibration(float zeroValue, float spanValue) { + _calibrationData.doZeroCalibration = zeroValue; + _calibrationData.doSpanCalibration = spanValue; + _calibrationData.isCalibrated[2] = true; + applyCalibration(); +} + +void CalibrationService::setOrpCalibration(float orpValue) { + _calibrationData.orpCalibration = orpValue; + _calibrationData.isCalibrated[4] = true; + applyCalibration(); +} + +CalibrationData CalibrationService::getCalibrationData() { + return _calibrationData; +} + +void CalibrationService::saveCalibrationData() { + EEPROM.put(0, _calibrationData); + Serial.println(F("Calibration data saved to EEPROM")); +} + +void CalibrationService::loadCalibrationData() { + EEPROM.get(0, _calibrationData); + Serial.println(F("Calibration data loaded from EEPROM")); +} + +void CalibrationService::printCalibrationInstructions() { + switch (_currentCalibrationType) { + case CALIBRATION_PH_3POINT: + Serial.println(F("PH 3-Point Calibration:")); + Serial.println(F("1. Place probe in pH 7 buffer")); + Serial.println(F("2. Wait for stable reading (10s)")); + Serial.println(F("3. Repeat for pH 4 and pH 10 buffers")); + break; + case CALIBRATION_EC_1POINT: + Serial.println(F("EC 1-Point Calibration:")); + Serial.println(F("1. Place probe in 1413 uS/cm solution")); + Serial.println(F("2. Wait for stable reading (10s)")); + break; + case CALIBRATION_DO_2POINT: + Serial.println(F("DO 2-Point Calibration:")); + Serial.println(F("1. Place probe in zero oxygen solution")); + Serial.println(F("2. Wait for stable reading (10s)")); + Serial.println(F("3. Place probe in air-saturated water")); + Serial.println(F("4. Wait for stable reading (10s)")); + break; + case CALIBRATION_ORP_1POINT: + Serial.println(F("ORP 1-Point Calibration:")); + Serial.println(F("1. Place probe in 225 mV solution")); + Serial.println(F("2. Wait for stable reading (10s)")); + break; + } +} + +void CalibrationService::calibratePh3Point() { + Serial.println(F("Performing pH 3-point calibration...")); + // Implementation depends on sensor library integration + float ph7Reading = _sensorHub->getValueBySensorNumber(0); + float offset = 7.0 - ph7Reading; + setPhCalibration(offset, offset, offset); +} + +void CalibrationService::calibratePh2Point() { + Serial.println(F("Performing pH 2-point calibration...")); + // Similar to 3-point but with 2 buffers +} + +void CalibrationService::calibrateEc1Point() { + Serial.println(F("Performing EC 1-point calibration...")); + float ecReading = _sensorHub->getValueBySensorNumber(3); + float factor = 1413.0 / ecReading; + setEcCalibration(factor); +} + +void CalibrationService::calibrateDo2Point() { + Serial.println(F("Performing DO 2-point calibration...")); + float zeroReading = _sensorHub->getValueBySensorNumber(2); + float spanReading = _sensorHub->getValueBySensorNumber(2); + setDoCalibration(zeroReading, spanReading); +} + +void CalibrationService::calibrateOrp1Point() { + Serial.println(F("Performing ORP 1-point calibration...")); + float orpReading = _sensorHub->getValueBySensorNumber(4); + float offset = 225.0 - orpReading; + setOrpCalibration(offset); +} + +void CalibrationService::applyCalibration() { + Serial.println(F("Applying calibration data...")); + // Apply calibration factors to sensors +} + +void CalibrationService::resetCalibration() { + for (int i = 0; i < 5; i++) { + _calibrationData.isCalibrated[i] = false; + } + saveCalibrationData(); + Serial.println(F("Calibration data reset")); +} \ No newline at end of file diff --git a/ArduinoUnoDo/WaterMonitor/CalibrationService.h b/ArduinoUnoDo/WaterMonitor/CalibrationService.h new file mode 100644 index 0000000..32ea627 --- /dev/null +++ b/ArduinoUnoDo/WaterMonitor/CalibrationService.h @@ -0,0 +1,63 @@ +#pragma once + +#include "GravitySensorHub.h" +#include "SdService.h" + +enum CalibrationType { + CALIBRATION_PH_3POINT, + CALIBRATION_PH_2POINT, + CALIBRATION_EC_1POINT, + CALIBRATION_DO_2POINT, + CALIBRATION_ORP_1POINT +}; + +struct CalibrationData { + float ph7Calibration = 0.0; + float ph4Calibration = 0.0; + float ph10Calibration = 0.0; + float ecCalibration = 0.0; + float doZeroCalibration = 0.0; + float doSpanCalibration = 0.0; + float orpCalibration = 0.0; + bool isCalibrated[5] = {false, false, false, false, false}; +}; + +class CalibrationService { +public: + CalibrationService(GravitySensorHub* sensorHub, SdService* sdService); + + void setup(); + void loop(); + + void startCalibration(CalibrationType type); + void stopCalibration(); + bool isCalibrating(); + + void setPhCalibration(float ph7, float ph4, float ph10); + void setEcCalibration(float ecValue); + void setDoCalibration(float zeroValue, float spanValue); + void setOrpCalibration(float orpValue); + + CalibrationData getCalibrationData(); + void saveCalibrationData(); + void loadCalibrationData(); + + void printCalibrationInstructions(); + +private: + GravitySensorHub* _sensorHub; + SdService* _sdService; + CalibrationData _calibrationData; + bool _isCalibrating; + CalibrationType _currentCalibrationType; + unsigned long _calibrationStartTime; + + void calibratePh3Point(); + void calibratePh2Point(); + void calibrateEc1Point(); + void calibrateDo2Point(); + void calibrateOrp1Point(); + + void applyCalibration(); + void resetCalibration(); +}; \ No newline at end of file diff --git a/ArduinoUnoDo/WaterMonitor/GravityDfr0553Adc.cpp b/ArduinoUnoDo/WaterMonitor/GravityDfr0553Adc.cpp new file mode 100644 index 0000000..9bea774 --- /dev/null +++ b/ArduinoUnoDo/WaterMonitor/GravityDfr0553Adc.cpp @@ -0,0 +1,102 @@ +#include "GravityDfr0553Adc.h" + +GravityDfr0553Adc::GravityDfr0553Adc() : + i2cAddress(0x48), + fullScaleMilliVolts(6144.0f), + available(false) +{ +} + +void GravityDfr0553Adc::setup(uint8_t address, float vccMillivolts) +{ + i2cAddress = address; + fullScaleMilliVolts = vccMillivolts; + + Wire.begin(); + Wire.beginTransmission(i2cAddress); + available = (Wire.endTransmission() == 0); +} + +bool GravityDfr0553Adc::isAvailable() const +{ + return available; +} + +float GravityDfr0553Adc::readMilliVolts(uint8_t channel, float fallbackMilliVolts) +{ + int16_t rawValue = 0; + if (!readRawSingleEnded(channel, rawValue)) + { + return fallbackMilliVolts; + } + + if (rawValue < 0) + { + rawValue = 0; + } + + return (rawValue * fullScaleMilliVolts) / 32768.0f; +} + +bool GravityDfr0553Adc::readRawSingleEnded(uint8_t channel, int16_t &rawValue) +{ + if (!available || channel > 3) + { + return false; + } + + const uint16_t mux = 0x04 + channel; + const uint16_t config = + 0x8000 | // Start single conversion. + (mux << 12) | // AIN0..AIN3 to GND. + 0x0000 | // +/-6.144V full-scale range for 0..VCC sensors. + 0x0100 | // Single-shot mode. + 0x0080 | // 128 samples per second. + 0x0003; // Disable comparator. + + if (!writeRegister(ConfigRegister, config)) + { + available = false; + return false; + } + + delay(9); + + uint16_t rawRegister = 0; + if (!readRegister(ConversionRegister, rawRegister)) + { + available = false; + return false; + } + + rawValue = static_cast(rawRegister); + return true; +} + +bool GravityDfr0553Adc::writeRegister(uint8_t reg, uint16_t value) +{ + Wire.beginTransmission(i2cAddress); + Wire.write(reg); + Wire.write(static_cast(value >> 8)); + Wire.write(static_cast(value & 0xFF)); + return Wire.endTransmission() == 0; +} + +bool GravityDfr0553Adc::readRegister(uint8_t reg, uint16_t &value) +{ + Wire.beginTransmission(i2cAddress); + Wire.write(reg); + if (Wire.endTransmission() != 0) + { + return false; + } + + if (Wire.requestFrom(i2cAddress, static_cast(2)) != 2) + { + return false; + } + + value = static_cast(Wire.read()) << 8; + value |= static_cast(Wire.read()); + return true; +} diff --git a/ArduinoUnoDo/WaterMonitor/GravityDfr0553Adc.h b/ArduinoUnoDo/WaterMonitor/GravityDfr0553Adc.h new file mode 100644 index 0000000..af9edee --- /dev/null +++ b/ArduinoUnoDo/WaterMonitor/GravityDfr0553Adc.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +class GravityDfr0553Adc +{ +public: + GravityDfr0553Adc(); + + void setup(uint8_t address, float vccMillivolts); + bool isAvailable() const; + float readMilliVolts(uint8_t channel, float fallbackMilliVolts); + +private: + static const uint8_t ConversionRegister = 0x00; + static const uint8_t ConfigRegister = 0x01; + + uint8_t i2cAddress; + float fullScaleMilliVolts; + bool available; + + bool writeRegister(uint8_t reg, uint16_t value); + bool readRegister(uint8_t reg, uint16_t &value); + bool readRawSingleEnded(uint8_t channel, int16_t &rawValue); +}; diff --git a/ArduinoUnoDo/WaterMonitor/GravityEc.cpp b/ArduinoUnoDo/WaterMonitor/GravityEc.cpp index 59c5e2d..3ed3e52 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityEc.cpp +++ b/ArduinoUnoDo/WaterMonitor/GravityEc.cpp @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityEc.cpp * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), @@ -20,17 +20,28 @@ **********************************************************************/ #include "GravityEc.h" +#include "GravityDfr0553Adc.h" #include "Arduino.h" +#include "config.h" -GravityEc::GravityEc(ISensor* temp) :ecSensorPin(A1), ECcurrent(0), index(0), AnalogAverage(0), +GravityEc::GravityEc(ISensor* temp) :ecSensorPin(EC_PIN), ECcurrent(0), index(0), AnalogAverage(0), AnalogValueTotal(0), averageVoltage(0), AnalogSampleTime(0), printTime(0),sum(0), tempSampleTime(0), AnalogSampleInterval(25),printInterval(700) { this->ecTemperature = temp; } +GravityEc::GravityEc(ISensor* temp, GravityDfr0553Adc* adc, uint8_t adcChannel) :ecSensorPin(EC_PIN), ECcurrent(0), index(0), AnalogAverage(0), +AnalogValueTotal(0), averageVoltage(0), AnalogSampleTime(0), printTime(0),sum(0), +tempSampleTime(0), AnalogSampleInterval(25),printInterval(700) +{ + this->ecTemperature = temp; + this->adc = adc; + this->adcChannel = adcChannel; +} + GravityEc::~GravityEc() { @@ -102,6 +113,10 @@ void GravityEc::calculateEc() { printTime = millis(); averageVoltage = AnalogAverage*5000.0 / 1024.0; + if (this->adc && this->adc->isAvailable()) + { + averageVoltage = this->adc->readMilliVolts(this->adcChannel, averageVoltage); + } double TempCoefficient = 1.0 + 0.0185*(this->ecTemperature->getValue() - 25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.0185*(fTP-25.0)); double CoefficientVolatge = (double)averageVoltage / TempCoefficient; diff --git a/ArduinoUnoDo/WaterMonitor/GravityEc.h b/ArduinoUnoDo/WaterMonitor/GravityEc.h index d278ee1..77b0dcd 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityEc.h +++ b/ArduinoUnoDo/WaterMonitor/GravityEc.h @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityEc.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), @@ -23,6 +23,8 @@ #include "GravityTemperature.h" #include "ISensor.h" +class GravityDfr0553Adc; + // external GravityTemperature ecTemperature; class GravityEc:public ISensor @@ -37,6 +39,7 @@ class GravityEc:public ISensor public: GravityEc(ISensor*); + GravityEc(ISensor*, GravityDfr0553Adc*, uint8_t); ~GravityEc(); // initialization @@ -51,6 +54,8 @@ class GravityEc:public ISensor private: // point to the temperature sensor pointer ISensor* ecTemperature = NULL; + GravityDfr0553Adc* adc = NULL; + uint8_t adcChannel = 0; static const int numReadings = 5; @@ -72,4 +77,3 @@ class GravityEc:public ISensor // Calculate the conductivity void calculateEc(); }; - diff --git a/ArduinoUnoDo/WaterMonitor/GravityOrp.cpp b/ArduinoUnoDo/WaterMonitor/GravityOrp.cpp index 1302372..2c4be37 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityOrp.cpp +++ b/ArduinoUnoDo/WaterMonitor/GravityOrp.cpp @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityOrp.cpp * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), @@ -20,9 +20,19 @@ **********************************************************************/ #include "GravityOrp.h" +#include "GravityDfr0553Adc.h" +#include "config.h" -GravityOrp::GravityOrp():orpSensorPin(A3), voltage(5.0), offset(0), orpValue(0.0), sum(0) +GravityOrp::GravityOrp():orpSensorPin(ORP_PIN), voltage(5.0), offset(0), +adc(NULL), adcChannel(0), +orpValue(0.0), sum(0) +{ +} + +GravityOrp::GravityOrp(GravityDfr0553Adc* adc, uint8_t adcChannel):orpSensorPin(ORP_PIN), voltage(5.0), offset(0), +adc(adc), adcChannel(adcChannel), +orpValue(0.0), sum(0) { } @@ -62,6 +72,12 @@ void GravityOrp::update() this->sum += orpArray[i]; averageOrp = this->sum / arrayLength; this->sum = 0; + if (this->adc && this->adc->isAvailable()) + { + averageOrp = this->adc->readMilliVolts(this->adcChannel, averageOrp*this->voltage * 1000 / 1024); + this->orpValue = ((30 * this->voltage * 1000) - (75 * averageOrp)) / 75 - this->offset; + return; + } //convert the analog value to orp according the circuit this->orpValue = ((30 * this->voltage * 1000) - (75 * averageOrp*this->voltage * 1000 / 1024)) / 75 - this->offset; } diff --git a/ArduinoUnoDo/WaterMonitor/GravityOrp.h b/ArduinoUnoDo/WaterMonitor/GravityOrp.h index 3c42b7a..24c2f28 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityOrp.h +++ b/ArduinoUnoDo/WaterMonitor/GravityOrp.h @@ -1,4 +1,4 @@ -/********************************************************************************* +/********************************************************************************* * GravityOrp.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), @@ -22,6 +22,9 @@ #pragma once #include #include "ISensor.h" + +class GravityDfr0553Adc; + class GravityOrp:public ISensor { public: @@ -34,6 +37,9 @@ class GravityOrp:public ISensor // Calibrate the offset float offset; private: + GravityDfr0553Adc* adc; + uint8_t adcChannel; + // orp value double orpValue; @@ -48,6 +54,7 @@ class GravityOrp:public ISensor public: GravityOrp(); + GravityOrp(GravityDfr0553Adc* adc, uint8_t adcChannel); ~GravityOrp(); // initialize the sensor @@ -59,4 +66,3 @@ class GravityOrp:public ISensor // Get the sensor data double getValue(); }; - diff --git a/ArduinoUnoDo/WaterMonitor/GravityPh.cpp b/ArduinoUnoDo/WaterMonitor/GravityPh.cpp index d76a716..766d2c9 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityPh.cpp +++ b/ArduinoUnoDo/WaterMonitor/GravityPh.cpp @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityPh.cpp * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), @@ -20,9 +20,18 @@ **********************************************************************/ #include "GravityPh.h" +#include "GravityDfr0553Adc.h" +#include "config.h" -GravityPh::GravityPh():phSensorPin(A2), offset(0.0f), +GravityPh::GravityPh():phSensorPin(PH_PIN), offset(0.0f), +adc(NULL), adcChannel(0), +samplingInterval(30),pHValue(0),voltage(0), sum(0) +{ +} + +GravityPh::GravityPh(GravityDfr0553Adc* adc, uint8_t adcChannel):phSensorPin(PH_PIN), offset(0.0f), +adc(adc), adcChannel(adcChannel), samplingInterval(30),pHValue(0),voltage(0), sum(0) { } @@ -59,6 +68,10 @@ void GravityPh::update() averageVoltage = this->sum / arrayLength; this->sum = 0; voltage = averageVoltage*5.0 / 1024.0; + if (this->adc && this->adc->isAvailable()) + { + voltage = this->adc->readMilliVolts(this->adcChannel, voltage * 1000.0f) / 1000.0f; + } pHValue = 3.5*voltage + this->offset; } @@ -74,5 +87,3 @@ double GravityPh::getValue() { return this->pHValue; } - - diff --git a/ArduinoUnoDo/WaterMonitor/GravityPh.h b/ArduinoUnoDo/WaterMonitor/GravityPh.h index 3d168bc..454145a 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityPh.h +++ b/ArduinoUnoDo/WaterMonitor/GravityPh.h @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityPh.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), @@ -22,6 +22,9 @@ #pragma once #include #include "ISensor.h" + +class GravityDfr0553Adc; + class GravityPh:public ISensor { public: @@ -34,6 +37,8 @@ class GravityPh:public ISensor // Take the sample interval int samplingInterval; private: + GravityDfr0553Adc* adc; + uint8_t adcChannel; static const int arrayLength = 5; int pHArray [arrayLength]; // stores the average value of the sensor return data double pHValue, voltage; @@ -42,6 +47,7 @@ class GravityPh:public ISensor public: GravityPh(); + GravityPh(GravityDfr0553Adc* adc, uint8_t adcChannel); ~GravityPh() {}; // initialization void setup (); @@ -52,4 +58,3 @@ class GravityPh:public ISensor // Get the sensor data double getValue(); }; - diff --git a/ArduinoUnoDo/WaterMonitor/GravityRtc.cpp b/ArduinoUnoDo/WaterMonitor/GravityRtc.cpp index 13f6221..71c33cc 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityRtc.cpp +++ b/ArduinoUnoDo/WaterMonitor/GravityRtc.cpp @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityRtc.cpp * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/GravityRtc.h b/ArduinoUnoDo/WaterMonitor/GravityRtc.h index ad3a57e..391421e 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityRtc.h +++ b/ArduinoUnoDo/WaterMonitor/GravityRtc.h @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityRtc.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/GravitySensorHub.cpp b/ArduinoUnoDo/WaterMonitor/GravitySensorHub.cpp index d0d3285..9c84696 100644 --- a/ArduinoUnoDo/WaterMonitor/GravitySensorHub.cpp +++ b/ArduinoUnoDo/WaterMonitor/GravitySensorHub.cpp @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************* * GravitySensorHub.cpp * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), @@ -41,16 +41,16 @@ GravitySensorHub::GravitySensorHub() this->sensors[i] = NULL; } - this->sensors[phSensor] = new GravityPh(); + this->sensors[phSensor] = new GravityPh(&this->adc, DFR0553_PH_CHANNEL); this->sensors[temperatureSensor] = new GravityTemperature(TEMPPIN); this->sensors[doSensor] = new SensorDo(); #ifdef SELECTEC - this->sensors[ecSensor] = new GravityEc(this->sensors[temperatureSensor]); + this->sensors[ecSensor] = new GravityEc(this->sensors[temperatureSensor], &this->adc, DFR0553_EC_CHANNEL); #else // Note: ArduinoUnoDo version uses GravityEc for both EC and TDS - this->sensors[ecSensor] = new GravityEc(this->sensors[temperatureSensor]); + this->sensors[ecSensor] = new GravityEc(this->sensors[temperatureSensor], &this->adc, DFR0553_EC_CHANNEL); #endif - this->sensors[orpSensor] = new GravityOrp(); + this->sensors[orpSensor] = new GravityOrp(&this->adc, DFR0553_ORP_CHANNEL); } @@ -76,6 +76,10 @@ GravitySensorHub::~GravitySensorHub() //******************************************************************************************** void GravitySensorHub::setup() { +#if ENABLE_DFR0553_ADC + this->adc.setup(DFR0553_I2C_ADDRESS, DFR0553_FULL_SCALE_MV); +#endif + for (size_t i = 0; i < SensorCount; i++) { if (this->sensors[i]) diff --git a/ArduinoUnoDo/WaterMonitor/GravitySensorHub.h b/ArduinoUnoDo/WaterMonitor/GravitySensorHub.h index 90d076a..6a85085 100644 --- a/ArduinoUnoDo/WaterMonitor/GravitySensorHub.h +++ b/ArduinoUnoDo/WaterMonitor/GravitySensorHub.h @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravitySensorHub.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), @@ -17,6 +17,7 @@ #pragma once #include "ISensor.h" +#include "GravityDfr0553Adc.h" #include "config.h" /* sensors : @@ -28,6 +29,7 @@ class GravitySensorHub { private: static const int SensorCount = SENSORCOUNT; + GravityDfr0553Adc adc; public: //******************************************************************************************** diff --git a/ArduinoUnoDo/WaterMonitor/GravityTemperature.cpp b/ArduinoUnoDo/WaterMonitor/GravityTemperature.cpp index 67e7ee0..0344e52 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityTemperature.cpp +++ b/ArduinoUnoDo/WaterMonitor/GravityTemperature.cpp @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityTemperature.cpp * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/GravityTemperature.h b/ArduinoUnoDo/WaterMonitor/GravityTemperature.h index 69db09c..a2acab4 100644 --- a/ArduinoUnoDo/WaterMonitor/GravityTemperature.h +++ b/ArduinoUnoDo/WaterMonitor/GravityTemperature.h @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * GravityTemperature.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/ISensor.h b/ArduinoUnoDo/WaterMonitor/ISensor.h index 405359c..f5ad725 100644 --- a/ArduinoUnoDo/WaterMonitor/ISensor.h +++ b/ArduinoUnoDo/WaterMonitor/ISensor.h @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * ISensor.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/IoTService.cpp b/ArduinoUnoDo/WaterMonitor/IoTService.cpp new file mode 100644 index 0000000..9088ed5 --- /dev/null +++ b/ArduinoUnoDo/WaterMonitor/IoTService.cpp @@ -0,0 +1,106 @@ +#include "IoTService.h" +#include "config.h" + +IoTService::IoTService() : + mqttClient(wifiClient), + lastPublishTime(0), + publishInterval(60000), // Default 1 minute + calibrationMode(false) { +} + +void IoTService::setup(const char* ssid, const char* password, const char* mqtt_server, int mqtt_port) { + _ssid = ssid; + _password = password; + _mqtt_server = mqtt_server; + _mqtt_port = mqtt_port; + + connectWiFi(); + mqttClient.setServer(_mqtt_server, _mqtt_port); +} + +void IoTService::loop() { + if (!mqttClient.connected()) { + reconnect(); + } + mqttClient.loop(); +} + +bool IoTService::isConnected() { + return WiFi.status() == WL_CONNECTED && mqttClient.connected(); +} + +void IoTService::publishData(float ph, float temp, float doValue, float ec, float orp) { + if (!isConnected()) return; + + unsigned long currentTime = millis(); + if (currentTime - lastPublishTime < publishInterval) return; + + StaticJsonDocument<200> doc; + doc["timestamp"] = millis(); + doc["ph"] = ph; + doc["temperature"] = temp; + doc["dissolved_oxygen"] = doValue; + doc["conductivity"] = ec; + doc["orp"] = orp; + doc["calibration_mode"] = calibrationMode; + + char jsonBuffer[200]; + serializeJson(doc, jsonBuffer); + + mqttClient.publish("knowflow/data", jsonBuffer); + lastPublishTime = currentTime; +} + +void IoTService::setPublishInterval(unsigned long interval) { + publishInterval = interval; +} + +void IoTService::setCalibrationMode(bool enabled) { + calibrationMode = enabled; +} + +void IoTService::connectWiFi() { + WiFi.begin(_ssid, _password); + + Serial.print("Connecting to WiFi"); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(); + Serial.print("Connected! IP address: "); + Serial.println(WiFi.localIP()); +} + +void IoTService::connectMQTT() { + mqttClient.setServer(_mqtt_server, _mqtt_port); + + while (!mqttClient.connected()) { + Serial.print("Attempting MQTT connection..."); + if (mqttClient.connect("KnowFlowClient")) { + Serial.println("connected"); + mqttClient.subscribe("knowflow/commands"); + } else { + Serial.print("failed, rc="); + Serial.print(mqttClient.state()); + Serial.println(" try again in 5 seconds"); + delay(5000); + } + } +} + +void IoTService::reconnect() { + while (!mqttClient.connected()) { + Serial.print("Attempting MQTT connection..."); + if (mqttClient.connect("KnowFlowClient")) { + Serial.println("connected"); + mqttClient.subscribe("knowflow/commands"); + } else { + Serial.print("failed, rc="); + Serial.print(mqttClient.state()); + Serial.println(" try again in 5 seconds"); + delay(5000); + } + } +} \ No newline at end of file diff --git a/ArduinoUnoDo/WaterMonitor/IoTService.h b/ArduinoUnoDo/WaterMonitor/IoTService.h new file mode 100644 index 0000000..f44084b --- /dev/null +++ b/ArduinoUnoDo/WaterMonitor/IoTService.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +class IoTService { +public: + IoTService(); + void setup(const char* ssid, const char* password, const char* mqtt_server, int mqtt_port = 1883); + void loop(); + bool isConnected(); + void publishData(float ph, float temp, float doValue, float ec, float orp); + void setPublishInterval(unsigned long interval); + void setCalibrationMode(bool enabled); + +private: + WiFiClient wifiClient; + PubSubClient mqttClient; + unsigned long lastPublishTime; + unsigned long publishInterval; + bool calibrationMode; + + void connectWiFi(); + void connectMQTT(); + void reconnect(); + + // WiFi credentials + const char* _ssid; + const char* _password; + const char* _mqtt_server; + int _mqtt_port; +}; \ No newline at end of file diff --git a/ArduinoUnoDo/WaterMonitor/PowerManager.cpp b/ArduinoUnoDo/WaterMonitor/PowerManager.cpp new file mode 100644 index 0000000..46ff028 --- /dev/null +++ b/ArduinoUnoDo/WaterMonitor/PowerManager.cpp @@ -0,0 +1,224 @@ +#include "PowerManager.h" +#include "config.h" + +PowerManager::PowerManager() : + _currentMode(POWER_MODE_NORMAL), + _lastWakeTime(0), + _lastBatteryCheck(0) { +} + +void PowerManager::setup(PowerConfig config) { + _config = config; + _lastWakeTime = millis(); + + // Configure power control pins + pinMode(SENSOR_POWER_PIN, OUTPUT); + pinMode(SOLAR_CHARGE_PIN, OUTPUT); + pinMode(BATTERY_MONITOR_PIN, INPUT); + + // Enable sensors by default + enableSensorPower(true); + + // Configure solar charging + if (_config.enableSolarCharging) { + enableSolarCharging(true); + } + + Serial.println(F("Power manager initialized")); + Serial.print(F("Sleep interval: ")); + Serial.print(_config.sleepInterval / 1000); + Serial.println(F(" seconds")); +} + +void PowerManager::loop() { + unsigned long currentTime = millis(); + + // Check battery status periodically + if (_config.enableBatteryMonitoring && + (currentTime - _lastBatteryCheck > 30000)) { // Check every 30 seconds + float voltage = getBatteryVoltage(); + int percentage = getBatteryPercentage(); + + Serial.print(F("Battery: ")); + Serial.print(voltage); + Serial.print(F("V (")); + Serial.print(percentage); + Serial.println(F("%)")); + + if (isCriticalBattery()) { + Serial.println(F("CRITICAL: Battery level critical!")); + enterPowerDownMode(); + } else if (isLowBattery()) { + Serial.println(F("WARNING: Battery level low")); + } + + _lastBatteryCheck = currentTime; + } + + // Auto sleep based on interval + if (_currentMode == POWER_MODE_NORMAL && + (currentTime - _lastWakeTime > _config.sleepInterval)) { + enterSleepMode(); + } +} + +void PowerManager::enterSleepMode() { + Serial.println(F("Entering sleep mode...")); + _currentMode = POWER_MODE_SLEEP; + + powerDownSensors(); + configureSleepMode(); + + // Sleep for configured interval +#if !defined(ARDUINO_ARCH_ESP32) + delay(_config.sleepInterval); +#endif + + wakeUp(); +} + +void PowerManager::enterDeepSleepMode() { + Serial.println(F("Entering deep sleep mode...")); + _currentMode = POWER_MODE_DEEP_SLEEP; + + powerDownSensors(); + configureDeepSleepMode(); + + // Use watchdog timer for wake-up + // This is a simplified version - actual implementation would use WDT + delay(_config.sleepInterval); + + wakeUp(); +} + +void PowerManager::enterPowerDownMode() { + Serial.println(F("Entering power down mode...")); + _currentMode = POWER_MODE_POWER_DOWN; + + powerDownSensors(); + configurePowerDownMode(); + + // Wait for external interrupt or manual reset + // In real implementation, this would use external wake-up + delay(_config.sleepInterval * 10); // Much longer sleep + + wakeUp(); +} + +void PowerManager::wakeUp() { + Serial.println(F("Waking up...")); + _currentMode = POWER_MODE_NORMAL; + _lastWakeTime = millis(); + + powerUpSensors(); + + // Allow sensors to stabilize + delay(1000); +} + +float PowerManager::getBatteryVoltage() { + int rawValue = analogRead(BATTERY_MONITOR_PIN); + float voltage = (rawValue * VREF / 1023.0) / VOLTAGE_DIVIDER; + return voltage; +} + +int PowerManager::getBatteryPercentage() { + float voltage = getBatteryVoltage(); + + // Simple linear mapping (3.0V = 0%, 4.2V = 100%) + float percentage = ((voltage - _config.criticalBatteryThreshold) / + (_config.lowBatteryThreshold - _config.criticalBatteryThreshold)) * 100.0; + + return constrain(percentage, 0, 100); +} + +bool PowerManager::isLowBattery() { + return getBatteryVoltage() < _config.lowBatteryThreshold; +} + +bool PowerManager::isCriticalBattery() { + return getBatteryVoltage() < _config.criticalBatteryThreshold; +} + +void PowerManager::setSleepInterval(unsigned long interval) { + _config.sleepInterval = interval; +} + +void PowerManager::enableSensorPower(bool enable) { + digitalWrite(SENSOR_POWER_PIN, enable ? HIGH : LOW); +} + +void PowerManager::enableSolarCharging(bool enable) { + digitalWrite(SOLAR_CHARGE_PIN, enable ? HIGH : LOW); +} + +PowerMode PowerManager::getCurrentPowerMode() { + return _currentMode; +} + +void PowerManager::powerDownSensors() { + enableSensorPower(false); + + // Disable ADC to save power +#if defined(ARDUINO_ARCH_AVR) + ADCSRA = 0; +#endif + + Serial.println(F("Sensors powered down")); +} + +void PowerManager::powerUpSensors() { + enableSensorPower(true); + + // Re-enable ADC +#if defined(ARDUINO_ARCH_AVR) + ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); +#endif + + Serial.println(F("Sensors powered up")); +} + +void PowerManager::configureSleepMode() { +#if defined(ARDUINO_ARCH_AVR) + // Set sleep mode to IDLE + set_sleep_mode(SLEEP_MODE_IDLE); + sleep_enable(); + sleep_cpu(); + sleep_disable(); +#elif defined(ARDUINO_ARCH_ESP32) + esp_sleep_enable_timer_wakeup(_config.sleepInterval * 1000ULL); + esp_light_sleep_start(); +#else + delay(_config.sleepInterval); +#endif +} + +void PowerManager::configureDeepSleepMode() { +#if defined(ARDUINO_ARCH_AVR) + // Set sleep mode to POWER_SAVE + set_sleep_mode(SLEEP_MODE_PWR_SAVE); + sleep_enable(); + sleep_cpu(); + sleep_disable(); +#elif defined(ARDUINO_ARCH_ESP32) + esp_sleep_enable_timer_wakeup(_config.sleepInterval * 1000ULL); + esp_deep_sleep_start(); +#else + delay(_config.sleepInterval); +#endif +} + +void PowerManager::configurePowerDownMode() { +#if defined(ARDUINO_ARCH_AVR) + // Set sleep mode to POWER_DOWN (lowest power) + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_enable(); + sleep_cpu(); + sleep_disable(); +#elif defined(ARDUINO_ARCH_ESP32) + esp_sleep_enable_timer_wakeup(_config.sleepInterval * 10ULL * 1000ULL); + esp_deep_sleep_start(); +#else + delay(_config.sleepInterval * 10); +#endif +} diff --git a/ArduinoUnoDo/WaterMonitor/PowerManager.h b/ArduinoUnoDo/WaterMonitor/PowerManager.h new file mode 100644 index 0000000..b460ad0 --- /dev/null +++ b/ArduinoUnoDo/WaterMonitor/PowerManager.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#if defined(ARDUINO_ARCH_AVR) +#include +#include +#elif defined(ARDUINO_ARCH_ESP32) +#include +#endif + +enum PowerMode { + POWER_MODE_NORMAL, + POWER_MODE_SLEEP, + POWER_MODE_DEEP_SLEEP, + POWER_MODE_POWER_DOWN +}; + +struct PowerConfig { + unsigned long sleepInterval = 60000; // Default 1 minute + bool enableSensorPowerControl = true; + bool enableBatteryMonitoring = true; + bool enableSolarCharging = false; + float lowBatteryThreshold = 3.3; // Volts + float criticalBatteryThreshold = 3.0; // Volts +}; + +class PowerManager { +public: + PowerManager(); + + void setup(PowerConfig config); + void loop(); + + void enterSleepMode(); + void enterDeepSleepMode(); + void enterPowerDownMode(); + void wakeUp(); + + float getBatteryVoltage(); + int getBatteryPercentage(); + bool isLowBattery(); + bool isCriticalBattery(); + + void setSleepInterval(unsigned long interval); + void enableSensorPower(bool enable); + void enableSolarCharging(bool enable); + + PowerMode getCurrentPowerMode(); + +private: + PowerConfig _config; + PowerMode _currentMode; + unsigned long _lastWakeTime; + unsigned long _lastBatteryCheck; + + void powerDownSensors(); + void powerUpSensors(); + void configureSleepMode(); + void configureDeepSleepMode(); + void configurePowerDownMode(); + + // Battery voltage calculation + static constexpr float VREF = 5.0; + static constexpr float VOLTAGE_DIVIDER = 0.5; // Assuming 1:1 voltage divider +}; diff --git a/ArduinoUnoDo/WaterMonitor/SdService.cpp b/ArduinoUnoDo/WaterMonitor/SdService.cpp index 80ef1b2..60cad35 100644 --- a/ArduinoUnoDo/WaterMonitor/SdService.cpp +++ b/ArduinoUnoDo/WaterMonitor/SdService.cpp @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * SdService.cpp * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/SdService.h b/ArduinoUnoDo/WaterMonitor/SdService.h index 3eace48..e3a375f 100644 --- a/ArduinoUnoDo/WaterMonitor/SdService.h +++ b/ArduinoUnoDo/WaterMonitor/SdService.h @@ -1,4 +1,4 @@ -/********************************************************************* +/********************************************************************* * SdService.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/SensorDo.cpp b/ArduinoUnoDo/WaterMonitor/SensorDo.cpp index 0048959..0d4d776 100644 --- a/ArduinoUnoDo/WaterMonitor/SensorDo.cpp +++ b/ArduinoUnoDo/WaterMonitor/SensorDo.cpp @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************* * SensorDo.cpp * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/SensorDo.h b/ArduinoUnoDo/WaterMonitor/SensorDo.h index 4c00fb1..ca9550b 100644 --- a/ArduinoUnoDo/WaterMonitor/SensorDo.h +++ b/ArduinoUnoDo/WaterMonitor/SensorDo.h @@ -1,4 +1,4 @@ -/********************************************************************************************* +/********************************************************************************************* * SensorDo.h * * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com), diff --git a/ArduinoUnoDo/WaterMonitor/WaterMonitor.ino b/ArduinoUnoDo/WaterMonitor/WaterMonitor.ino index 2b803d4..a59f9a0 100644 --- a/ArduinoUnoDo/WaterMonitor/WaterMonitor.ino +++ b/ArduinoUnoDo/WaterMonitor/WaterMonitor.ino @@ -1,101 +1,203 @@ /********************************************************************* - * WaterMonitor.ino + * WaterMonitor.ino - Enhanced KnowFlow with IoT, Calibration, and Power Management * - * Copyright (C) 2017 [DFRobot](http://www.dfrobot.com) - * GitHub Link :https://github.com/DFRobot/watermonitor - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Copyright (C) 2024 KnowFlow Team + * Enhanced version with WiFi/IoT connectivity, calibration, and power management * * Description: - * This sample code is mainly used to monitor water quality - * including ph, temperature, dissolved oxygen, ec and orp,etc. + * Enhanced water quality monitoring system with: + * - WiFi/IoT connectivity via MQTT + * - Automatic sensor calibration + * - Power management with sleep modes + * - Battery monitoring and solar charging support + * - Real-time data transmission * - * Software Environment: Arduino IDE 1.8.2 - * Software download link: https://www.arduino.cc/en/Main/Software - * - * Install the library file: - * Copy the files from the github repository folder libraries to the libraries - * in the Arduino IDE 1.8.2 installation directory - * - * Hardware platform : Arduino M0 Or Arduino Mega2560 - * Sensor pin: - * EC : A1 - * PH : A2 - * ORP : A3 - * RTC : I2C - * DO : Serial port Rx(0),Tx(1) - * GravityDO:A4 - * temperature:D5 - * - * SD card attached to SPI bus as follows: - * Mega: MOSI - pin 51, MISO - pin 50, CLK - pin 52, CS - pin 53 - * and pin #53 (SS) must be an output - * M0: Onboard SPI pin,CS - pin 4 (CS pin can be changed) - * - * author : Jason(jason.ling@dfrobot.com) - * version : V1.0 - * date : 2017-04-06 + * Hardware platform : Arduino Uno with WiFi shield or ESP32 + * New features added : IoT connectivity, calibration, power management + * version : V2.0 + * date : 2024-07-21 **********************************************************************/ #include #include #include +#include +#include "config.h" + +// Core modules #include "GravitySensorHub.h" #include "GravityRtc.h" -#include "OneWire.h" #include "SdService.h" #include "Debug.h" -#include "config.h" -// clock module +// New enhanced features +#include "IoTService.h" +#include "CalibrationService.h" +#include "PowerManager.h" + +// Clock module GravityRtc rtc; -// sensor monitor +// Sensor monitoring GravitySensorHub sensorHub; SdService sdService = SdService(sensorHub.sensors); + +// New services +IoTService iotService; +CalibrationService calibrationService(&sensorHub, &sdService); +PowerManager powerManager; + +// System state +unsigned long lastUpdateTime = 0; +unsigned long updateInterval = 5000; // 5 seconds between readings +bool systemInitialized = false; + void setup() { - Serial.begin(9600); - rtc.setup(); - sensorHub.setup(); - sdService.setup(); + Serial.begin(DEBUG_BAUD_RATE); + Serial.println(F("KnowFlow Enhanced Water Monitor v2.0")); + Serial.println(F("=====================================")); -} + // Initialize EEPROM for calibration data + EEPROM.begin(512); + + // Initialize RTC + rtc.setup(); + // Initialize sensor hub + sensorHub.setup(); -//******************************************************************************************** -// function name: sensorHub.getValueBySensorNumber (0) -// Function Description: Get the sensor's values, and the different parameters represent the acquisition of different sensor data -// Parameters: 0 ph value -// Parameters: 1 temperature value -// Parameters: 2 Dissolved Oxygen -// Parameters: 3 Conductivity -// Parameters: 4 Redox potential -// return value: returns a double type of data -//******************************************************************************************** + // Initialize SD service + sdService.setup(); -unsigned long updateTime = 0; + // Initialize calibration service + calibrationService.setup(); + + // Initialize power manager + PowerConfig powerConfig; + powerConfig.sleepInterval = POWER_SLEEP_INTERVAL; + powerConfig.lowBatteryThreshold = LOW_BATTERY_THRESHOLD; + powerConfig.criticalBatteryThreshold = CRITICAL_BATTERY_THRESHOLD; + powerConfig.enableBatteryMonitoring = ENABLE_BATTERY_MONITORING; + powerConfig.enableSensorPowerControl = ENABLE_SENSOR_POWER_CONTROL; + powerConfig.enableSolarCharging = ENABLE_SOLAR_CHARGING; + + powerManager.setup(powerConfig); + + // Initialize IoT service (WiFi/MQTT) + Serial.print(F("Initializing IoT service...")); + iotService.setup(WIFI_SSID, WIFI_PASSWORD, MQTT_SERVER, MQTT_PORT); + Serial.println(F("OK")); + + systemInitialized = true; + Serial.println(F("System initialization complete!")); + Serial.println(F("=====================================")); +} void loop() { - rtc.update(); - sensorHub.update(); - sdService.update(); - - // ************************* Serial debugging ****************** - if(millis() - updateTime > 2000) - { - updateTime = millis(); - Serial.print(F("ph= ")); - Serial.print(sensorHub.getValueBySensorNumber(0)); - Serial.print(F(" Temp= ")); - Serial.print(sensorHub.getValueBySensorNumber(1)); - Serial.print(F(" Do= ")); - Serial.print(sensorHub.getValueBySensorNumber(2)); - Serial.print(F(" Ec= ")); - Serial.print(sensorHub.getValueBySensorNumber(3)); - Serial.print(F(" Orp= ")); - Serial.println(sensorHub.getValueBySensorNumber(4)); - } + if (!systemInitialized) return; + + unsigned long currentTime = millis(); + + // Update all services + rtc.update(); + sensorHub.update(); + sdService.update(); + calibrationService.loop(); + powerManager.loop(); + iotService.loop(); + + // Main update cycle + if (currentTime - lastUpdateTime > updateInterval) { + lastUpdateTime = currentTime; + + // Get all sensor readings + float ph = sensorHub.getValueBySensorNumber(0); + float temp = sensorHub.getValueBySensorNumber(1); + float doValue = sensorHub.getValueBySensorNumber(2); + float ec = sensorHub.getValueBySensorNumber(3); + float orp = sensorHub.getValueBySensorNumber(4); + + // Print readings + printSensorReadings(ph, temp, doValue, ec, orp); + + // Publish to IoT + if (iotService.isConnected()) { + iotService.publishData(ph, temp, doValue, ec, orp); + } + + // Check for calibration commands + handleSerialCommands(); + } } +void printSensorReadings(float ph, float temp, float doValue, float ec, float orp) { + Serial.println(F("=== Current Readings ===")); + Serial.print(F("pH: ")); + Serial.println(ph, 2); + Serial.print(F("Temperature: ")); + Serial.println(temp, 2); + Serial.print(F("DO: ")); + Serial.println(doValue, 2); + Serial.print(F("EC: ")); + Serial.println(ec, 2); + Serial.print(F("ORP: ")); + Serial.println(orp, 2); + + // Print battery status + Serial.print(F("Battery: ")); + Serial.print(powerManager.getBatteryVoltage(), 2); + Serial.print(F("V (")); + Serial.print(powerManager.getBatteryPercentage()); + Serial.println(F("%)")); + + Serial.print(F("WiFi: ")); + Serial.println(iotService.isConnected() ? F("Connected") : F("Disconnected")); + Serial.println(F("========================")); +} + +void handleSerialCommands() { + if (Serial.available()) { + String command = Serial.readStringUntil('\n'); + command.trim(); + + if (command == "calibrate_ph") { + Serial.println(F("Starting pH calibration...")); + calibrationService.startCalibration(CALIBRATION_PH_3POINT); + } else if (command == "calibrate_ec") { + Serial.println(F("Starting EC calibration...")); + calibrationService.startCalibration(CALIBRATION_EC_1POINT); + } else if (command == "calibrate_do") { + Serial.println(F("Starting DO calibration...")); + calibrationService.startCalibration(CALIBRATION_DO_2POINT); + } else if (command == "calibrate_orp") { + Serial.println(F("Starting ORP calibration...")); + calibrationService.startCalibration(CALIBRATION_ORP_1POINT); + } else if (command == "sleep") { + Serial.println(F("Entering sleep mode...")); + powerManager.enterSleepMode(); + } else if (command == "deep_sleep") { + Serial.println(F("Entering deep sleep mode...")); + powerManager.enterDeepSleepMode(); + } else if (command == "battery") { + Serial.print(F("Battery: ")); + Serial.print(powerManager.getBatteryVoltage()); + Serial.print(F("V (")); + Serial.print(powerManager.getBatteryPercentage()); + Serial.println(F("%)")); + } else if (command == "help") { + printHelp(); + } + } +} + +void printHelp() { + Serial.println(F("KnowFlow Enhanced Commands:")); + Serial.println(F(" calibrate_ph - Start pH 3-point calibration")); + Serial.println(F(" calibrate_ec - Start EC 1-point calibration")); + Serial.println(F(" calibrate_do - Start DO 2-point calibration")); + Serial.println(F(" calibrate_orp - Start ORP 1-point calibration")); + Serial.println(F(" sleep - Enter sleep mode")); + Serial.println(F(" deep_sleep - Enter deep sleep mode")); + Serial.println(F(" battery - Show battery status")); + Serial.println(F(" help - Show this help")); +} diff --git a/ArduinoUnoDo/WaterMonitor/config.h b/ArduinoUnoDo/WaterMonitor/config.h index 1452be7..df444cd 100644 --- a/ArduinoUnoDo/WaterMonitor/config.h +++ b/ArduinoUnoDo/WaterMonitor/config.h @@ -5,6 +5,29 @@ //#define DEBUG_AVR // For Arduino Uno, Mega2560 //#define DEBUG_M0 // For Arduino M0 +// WiFi Configuration +#define WIFI_SSID "YourWiFiNetwork" +#define WIFI_PASSWORD "YourWiFiPassword" +#define MQTT_SERVER "your.mqtt.server.com" +#define MQTT_PORT 1883 +#define MQTT_TOPIC "knowflow/data" +#define MQTT_CLIENT_ID "KnowFlowDevice" + +// Power Management Configuration +#define POWER_SLEEP_INTERVAL 60000 // milliseconds (1 minute) +#define LOW_BATTERY_THRESHOLD 3.3 // Volts +#define CRITICAL_BATTERY_THRESHOLD 3.0 // Volts +#define ENABLE_BATTERY_MONITORING true +#define ENABLE_SENSOR_POWER_CONTROL true +#define ENABLE_SOLAR_CHARGING false + +// Calibration Configuration +#define CALIBRATION_EEPROM_ADDR 0 +#define ENABLE_AUTO_CALIBRATION true +#define CALIBRATION_STABILITY_TIME 10000 // milliseconds +#define PHOFFSET 0.0 // pH calibration offset +#define ECKVALUE 1.0 // EC K value for calibration + // The maximum length of the sensor filter array #define ARRAYLENGTH 10 @@ -12,21 +35,55 @@ #define SDUPDATEDATATIME 60000 // EC sensor is selected by default, comment this line to use TDS sensor -#define SELECTEC +#define SELECTEC // Sensor pin settings // DO sensor uses Serial port Rx(0), Tx(1) for ArduinoUnoDo version -#define DOPIN A0 // Not used in this version (DO uses Serial) -#define ECPIN A1 -#define TDSPIN A1 -#define PHPIN A2 +#if defined(ARDUINO_ARCH_ESP32) +#define ECPIN 34 +#define PHPIN 35 +#define ORPPIN 32 +#define DOPIN 33 +#define BATTERY_MONITOR_PIN 36 +#else +#define DOPIN A0 // Not used in this version (DO uses Serial) +#define ECPIN A1 +#define PHPIN A2 #define ORPPIN A3 +#define BATTERY_MONITOR_PIN A7 +#endif +#define TDSPIN ECPIN #define TEMPPIN 5 +#define RTC_SDA A4 +#define RTC_SCL A5 -// Set sensor offset (calibration data) -// These values can be adjusted based on calibration -#define PHOFFSET 0.0 // pH calibration offset -#define ECKVALUE 1.0 // EC K value for calibration +// Compatibility aliases used by the enhanced WaterMonitor services. +#define EC_PIN ECPIN +#define PH_PIN PHPIN +#define ORP_PIN ORPPIN +#define DO_PIN DOPIN +#define TEMP_PIN TEMPPIN + +// DFRobot Gravity DFR0553 I2C ADS1115 16-bit ADC +#define ENABLE_DFR0553_ADC true +#define DFR0553_I2C_ADDRESS 0x48 +#define DFR0553_FULL_SCALE_MV 6144.0f +#define DFR0553_EC_CHANNEL 0 +#define DFR0553_PH_CHANNEL 1 +#define DFR0553_ORP_CHANNEL 2 +#define DFR0553_AUX_CHANNEL 3 + +// Power Control Pins +#define SENSOR_POWER_PIN 8 +#define SOLAR_CHARGE_PIN 9 + +// SD Card Configuration +#define SD_CS_PIN 4 +#define ENABLE_SD_LOGGING true + +// Debug Configuration +#define ENABLE_DEBUG true +#define DEBUG_BAUD_RATE 9600 // The maximum number of sensors #define SENSORCOUNT 5 diff --git a/README.md b/README.md index 059faa8..8889508 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,11 @@ DFRobot also offers a [KnowFlow starter kit.](https://www.dfrobot.com/product-16 * ORP (ORP probe and ORP circuit board) * Temperature (temperature probe and temperature circuit board) * Dissolved Oxygen (DO probe, BNC and circuit board) +#### Analog conversion: +* DFRobot Gravity: I2C ADS1115 16-Bit ADC Module (DFR0553) + * I2C address: `0x48` by default, or `0x49` when the module address switch is changed + * Input range: `0~VCC` + * Default channel mapping in firmware: `A0=EC`, `A1=pH`, `A2=ORP`, `A3=AUX` #### Data Storage: * Micro-SD module * Micro SD card @@ -138,6 +143,17 @@ This repository contains multiple firmware versions for different hardware confi - Select Tools → Port: (your USB port) - Click "Verify" then "Upload" +### DFR0553 ADC configuration + +The enhanced `ArduinoUnoDo/WaterMonitor` firmware supports the Gravity DFR0553 I2C 16-bit ADC module without requiring the external DFRobot ADS1115 library. Configure it in `ArduinoUnoDo/WaterMonitor/config.h`: + +* `ENABLE_DFR0553_ADC`: enable or disable the I2C ADC path. +* `DFR0553_I2C_ADDRESS`: default `0x48`; set to `0x49` if the hardware address switch is changed. +* `DFR0553_FULL_SCALE_MV`: default `6144.0f`, matching the ADS1115 `+/-6.144V` full-scale setting used for `0~VCC` single-ended sensor inputs. +* `DFR0553_EC_CHANNEL`, `DFR0553_PH_CHANNEL`, `DFR0553_ORP_CHANNEL`: map the EC, pH, and ORP analog front ends to ADS1115 channels. + +If the DFR0553 is not detected on I2C, the firmware falls back to the original MCU `analogRead()` path for the analog sensors. + ## FAQ ### Q: Why can't I verify the code? @@ -194,13 +210,14 @@ to github, so please forgive any stupid errors. Suggestions are welcome!) :) - [x] Support DO Sensor from DFRobot - [x] Modify the construction of the files system - [x] Code cleanup and configuration unification +- [x] Add DFR0553 I2C 16-bit ADC support for ArduinoUnoDo analog sensors - [ ] Add YouTube video tutorial - [ ] Website setup (www.knowflow.org) -- [ ] IOT feature +- [x] IOT feature for enhanced ArduinoUnoDo firmware - [x] Calibration function (partially implemented in ArduinoUnoGravityDo) - [x] ESP32 support (basic sensor functionality) - [ ] ESP32 WiFi/IOT features -- [ ] Low power function +- [x] Low power function for enhanced ArduinoUnoDo firmware ## Contact