Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 143 additions & 30 deletions DFRobot_PH.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
*
* version V1.0
* date 2018-04
*
* version V1.1
* date 2020-04
* Changes the memory addressing to allow multiple PH sensors
*/


#if ARDUINO >= 100
#include "Arduino.h"
#else
Expand All @@ -20,71 +23,181 @@
#include "DFRobot_PH.h"
#include <EEPROM.h>

#define EEPROM_write(address, p) {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) EEPROM.write(address+i, pp[i]);}
#define EEPROM_read(address, p) {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) pp[i]=EEPROM.read(address+i);}
#define EEPROM_write(address, value) {int i = 0; byte *pp = (byte*)&(value);for(; i < sizeof(value); i++) EEPROM.write(address+i, pp[i]);}
#define EEPROM_read(address, value) {int i = 0; byte *pp = (byte*)&(value);for(; i < sizeof(value); i++) pp[i]=EEPROM.read(address+i);}

#define PHVALUEADDR 0x00 //the start address of the pH calibration parameters stored in the EEPROM
// The start address of the pH calibration parameters stored in the EEPROM
#define PHVALUEADDR 0

// Maps the pin (A0,A1..A11) to a number 0-11 so the address can be determined
uint8_t mapPHPin(uint8_t phPin)
{
int addressMap = 0;
if (phPin == A0){
Serial.print(" pin A0 ");
addressMap = 0;
} else if (phPin == A1){
Serial.print(" pin A1 ");
addressMap = 1;
} else if (phPin == A2){
Serial.print(" pin A2 ");
addressMap = 2;
} else if (phPin == A3){
Serial.print(" pin A3 ");
addressMap = 3;
} else if (phPin == A4){
Serial.print(" pin A4 ");
addressMap = 4;
} else if (phPin == A5){
Serial.print(" pin A5 ");
addressMap = 5;
} else if (phPin == A6){
Serial.print(" pin A6 ");
addressMap = 6;
} else if (phPin == A7){
Serial.print(" pin A7 ");
addressMap = 7;
} else if (phPin == A8){
Serial.print(" pin A8 ");
addressMap = 8;
} else if (phPin == A9){
Serial.print(" pin A9 ");
addressMap = 9;
} else if (phPin == A10){
Serial.print(" pin A10 ");
addressMap = 10;
} else if (phPin == A11){
Serial.print(" pin A11 ");
addressMap = 11;
}

return addressMap;
}

// Default construtor to ensure backwards compatibility
DFRobot_PH::DFRobot_PH()
{
// As no pin was provided we will use the data for pin A0
this->_pin = A0;

// Set the address
// For each PH sensor we need to store 2 floats (pH 4.0 and pH 7.0)
// This will be 8byte per sensor and the largest arduino has 12 analogue ports
// So let's start at address 0 and go up by 8b for each analogue port. This will use upto 96b
// For the EC library we will start after PH addresses
this->_address = PHVALUEADDR + (sizeof(float) * 2 * mapPHPin(this->_pin));

// Buffer solution 4.0 at 25C
this->_acidVoltage = 2032.44;

// Buffer solution 7.0 at 25C
this->_neutralVoltage = 1500.0;

// Initialise the rest of the values with an initial starting value
this->_voltage = 1500.0;
this->_temperature = 25.0;
this->_phValue = 7.0;
this->_acidVoltage = 2032.44; //buffer solution 4.0 at 25C
this->_neutralVoltage = 1500.0; //buffer solution 7.0 at 25C
}

// Updated construtor to allow multiple pH sensors
DFRobot_PH::DFRobot_PH(uint8_t phPin)
{
// Set the pin to the supplied value
this->_pin = phPin;

// Set the address
// For each PH sensor we need to store 2 floats (pH 4.0 and pH 7.0)
// This will be 8byte per sensor and the largest arduino has 12 analogue ports
// So let's start at address 0 and go up by 8b for each analogue port. This will use upto 96b
// For the EC library we will start after PH addresses
this->_address = PHVALUEADDR + (sizeof(float) * 2 * mapPHPin(this->_pin));

// Buffer solution 4.0 at 25C
this->_acidVoltage = 2032.44;

// Buffer solution 7.0 at 25C
this->_neutralVoltage = 1500.0;

// Initialise the rest of the values with an initial starting value
this->_voltage = 1500.0;
this->_temperature = 25.0;
this->_phValue = 7.0;
}

// Default destructor
DFRobot_PH::~DFRobot_PH()
{

}

// Initialiser
void DFRobot_PH::begin()
{
EEPROM_read(PHVALUEADDR, this->_neutralVoltage); //load the neutral (pH = 7.0)voltage of the pH board from the EEPROM
Serial.print("_pin:");
Serial.println(this->_pin);

Serial.print("mapped:");
Serial.println(mapPHPin(this->_pin));

Serial.print("_address:");
Serial.println(this->_address);

// Load the neutral (pH = 7.0) voltage of the pH board from the EEPROM
EEPROM_read(this->_address, this->_neutralVoltage);
Serial.print("_neutralVoltage:");
Serial.println(this->_neutralVoltage);
if(EEPROM.read(PHVALUEADDR)==0xFF && EEPROM.read(PHVALUEADDR+1)==0xFF && EEPROM.read(PHVALUEADDR+2)==0xFF && EEPROM.read(PHVALUEADDR+3)==0xFF){
this->_neutralVoltage = 1500.0; // new EEPROM, write typical voltage
EEPROM_write(PHVALUEADDR, this->_neutralVoltage);

// If the values are all 255 then write a default value in
if(EEPROM.read(this->_address)==0xFF && EEPROM.read(this->_address+1)==0xFF && EEPROM.read(this->_address+2)==0xFF && EEPROM.read(this->_address+3)==0xFF){
// new EEPROM, write typical voltage for pH = 7.0
this->_neutralVoltage = 1500.0;
EEPROM_write(this->_address, this->_neutralVoltage);
}
EEPROM_read(PHVALUEADDR+4, this->_acidVoltage);//load the acid (pH = 4.0) voltage of the pH board from the EEPROM

// Load the acid (pH = 4.0) voltage of the pH board from the EEPROM
EEPROM_read(this->_address+4, this->_acidVoltage);
Serial.print("_acidVoltage:");
Serial.println(this->_acidVoltage);
if(EEPROM.read(PHVALUEADDR+4)==0xFF && EEPROM.read(PHVALUEADDR+5)==0xFF && EEPROM.read(PHVALUEADDR+6)==0xFF && EEPROM.read(PHVALUEADDR+7)==0xFF){
this->_acidVoltage = 2032.44; // new EEPROM, write typical voltage
EEPROM_write(PHVALUEADDR+4, this->_acidVoltage);

// If the values are all 255 then write a default value in
if(EEPROM.read(this->_address+4)==0xFF && EEPROM.read(this->_address+5)==0xFF && EEPROM.read(this->_address+6)==0xFF && EEPROM.read(this->_address+7)==0xFF){
// new EEPROM, write typical voltage for pH = 4.0
this->_acidVoltage = 2032.44;
EEPROM_write(this->_address+4, this->_acidVoltage);
}
}

// Function to read the pH
float DFRobot_PH::readPH(float voltage, float temperature)
{
float slope = (7.0-4.0)/((this->_neutralVoltage-1500.0)/3.0 - (this->_acidVoltage-1500.0)/3.0); // two point: (_neutralVoltage,7.0),(_acidVoltage,4.0)
float intercept = 7.0 - slope*(this->_neutralVoltage-1500.0)/3.0;
//Serial.print("slope:");
//Serial.print(slope);
//Serial.print(",intercept:");
//Serial.println(intercept);
this->_phValue = slope*(voltage-1500.0)/3.0+intercept; //y = k*x + b
// From the two point calibration: (_neutralVoltage, 7.0), (_acidVoltage, 4.0)
float slope = (7.0 - 4.0)/((this->_neutralVoltage - 1500.0) / 3.0 - (this->_acidVoltage - 1500.0) / 3.0);
float intercept = 7.0 - slope*(this->_neutralVoltage - 1500.0) / 3.0;

// Linear response y = k*x + b
this->_phValue = slope * (voltage - 1500.0) / 3.0 + intercept;

return _phValue;
}


void DFRobot_PH::calibration(float voltage, float temperature,char* cmd)
void DFRobot_PH::calibration(float voltage, float temperature, char* cmd)
{
this->_voltage = voltage;
this->_temperature = temperature;
strupr(cmd);
phCalibration(cmdParse(cmd)); // if received Serial CMD from the serial monitor, enter into the calibration mode

// If received Serial CMD from the serial monitor, enter into the calibration mode
phCalibration(cmdParse(cmd));
}

void DFRobot_PH::calibration(float voltage, float temperature)
{
this->_voltage = voltage;
this->_temperature = temperature;

// If received Serial CMD from the serial monitor, enter into the calibration mode
if(cmdSerialDataAvailable() > 0){
phCalibration(cmdParse()); // if received Serial CMD from the serial monitor, enter into the calibration mode
phCalibration(cmdParse());
}
}

Expand Down Expand Up @@ -129,10 +242,10 @@ byte DFRobot_PH::cmdParse()
byte modeIndex = 0;
if(strstr(this->_cmdReceivedBuffer, "ENTERPH") != NULL){
modeIndex = 1;
}else if(strstr(this->_cmdReceivedBuffer, "EXITPH") != NULL){
modeIndex = 3;
}else if(strstr(this->_cmdReceivedBuffer, "CALPH") != NULL){
modeIndex = 2;
}else if(strstr(this->_cmdReceivedBuffer, "EXITPH") != NULL){
modeIndex = 3;
}
return modeIndex;
}
Expand Down Expand Up @@ -160,7 +273,7 @@ void DFRobot_PH::phCalibration(byte mode)

case 2:
if(enterCalibrationFlag){
if((this->_voltage>1322)&&(this->_voltage<1678)){ // buffer solution:7.0{
if((this->_voltage>1322)&&(this->_voltage<1678)){ // buffer solution:7.0
Serial.println();
Serial.print(F(">>>Buffer Solution:7.0"));
this->_neutralVoltage = this->_voltage;
Expand Down Expand Up @@ -188,9 +301,9 @@ void DFRobot_PH::phCalibration(byte mode)
Serial.println();
if(phCalibrationFinish){
if((this->_voltage>1322)&&(this->_voltage<1678)){
EEPROM_write(PHVALUEADDR, this->_neutralVoltage);
EEPROM_write(this->_address, this->_neutralVoltage);
}else if((this->_voltage>1854)&&(this->_voltage<2210)){
EEPROM_write(PHVALUEADDR+4, this->_acidVoltage);
EEPROM_write(this->_address+4, this->_acidVoltage);
}
Serial.print(F(">>>Calibration Successful"));
}else{
Expand Down
42 changes: 35 additions & 7 deletions DFRobot_PH.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
*
* version V1.0
* date 2018-04
*
* version V1.1
* date 2020-04
* Changes the memory addressing to allow multiple PH sensors
*/

#ifndef _DFROBOT_PH_H_
Expand All @@ -19,33 +23,57 @@
#include "WProgram.h"
#endif

#define ReceivedBufferLength 10 //length of the Serial CMD buffer
// Length of the Serial CMD buffer
#define ReceivedBufferLength 10

/**
* Maps the pin (A0,A1..A11) to a number 0-11 so the address can be determined
* @param phPin the pin the pH sensor is attached to
* @return the address multiplier
*/
uint8_t mapPHPin(uint8_t phPin);

class DFRobot_PH
{
public:
// Construtors
DFRobot_PH();
DFRobot_PH(uint8_t phPin);

// Destructors
~DFRobot_PH();
void calibration(float voltage, float temperature,char* cmd); //calibration by Serial CMD

// Initialization
void begin();

// Calibration by Serial CMD
void calibration(float voltage, float temperature, char* cmd);
void calibration(float voltage, float temperature);
float readPH(float voltage, float temperature); // voltage to pH value, with temperature compensation
void begin(); //initialization

// Voltage to pH value, with temperature compensation
float readPH(float voltage, float temperature);

private:
uint8_t _pin;
int _address;
float _phValue;
float _acidVoltage;
float _neutralVoltage;
float _voltage;
float _temperature;

char _cmdReceivedBuffer[ReceivedBufferLength]; //store the Serial CMD
// Store the Serial CMD
char _cmdReceivedBuffer[ReceivedBufferLength];
byte _cmdReceivedBufferIndex;

private:
// Calibration process, wirte key parameters to EEPROM
void phCalibration(byte mode);

boolean cmdSerialDataAvailable();
void phCalibration(byte mode); // calibration process, wirte key parameters to EEPROM
byte cmdParse(const char* cmd);

byte cmdParse();
byte cmdParse(const char* cmd);
};

#endif
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## DFRobot_PH Library
---------------------------------------------------------
This is the sample code for Gravity: Analog pH Sensor / Meter Kit V2, SKU:SEN0161-V2

WARNING: There is no temperature compensation implemented in this library.
Although you can provide the temperature it is not used.

## Table of Contents

* [Methods](#methods)
Expand All @@ -13,14 +17,20 @@ This is the sample code for Gravity: Analog pH Sensor / Meter Kit V2, SKU:SEN016
## Methods

```C++
/*
* @brief The new optional constructor to allow multiple pH sensors
*
* @param phPin: The pin of the pH sensor i.e. A0
*/
DFRobot_PH(int phPin);

/*
* @brief Init The Analog pH Sensor
*/
void begin();

/*
* @brief Convert voltage to PH with temperature compensation
* @brief Convert voltage to PH
*
* @param voltage : Voltage value
* temperature : Ambient temperature
Expand Down Expand Up @@ -54,6 +64,10 @@ Meag2560 | √ | | |

- date 2018-11-6
- version V1.0
- Inital version
- date 2020-04-18
- version V1.1
- Update to allow multiple pH sensors each with their own calibration

## Credits

Expand Down