DFRobot PH sensor > Arduino > ESP8266 > MQTT

Moderators: grovkillen, Stuntteam, TD-er

Post Reply
Message
Author
sel303
New user
Posts: 8
Joined: 19 Oct 2016, 12:09

DFRobot PH sensor > Arduino > ESP8266 > MQTT

#1 Post by sel303 » 29 Oct 2016, 15:19

OK, so here i am everyone. Hacking together wires, punching keys on my keyboard like a caveman until something works. ESPeasy makes working with sensors extremely, well, easy. When it comes to actual programing I am pretty illiterate it seems. I definitely need to do some studying.
Here is what I am trying to achieve. Get PH Value from water into a database along with all of my other sensors.
I have this ph sensor > https://www.dfrobot.com/index.php?route ... ct_id=1110 connected to an TKLCD(Arduino Leonardo with LCD attached) The sensor is connected to A0 analog pin and then connected to ESP8266-12E through i2c. The ESP8266 is running espeasy and I am trying to get the PH values into MQTT with the Pro Mini Extender.
I can get accurate PH readings printed into the serial monitor of the arduino IDE and the LCD but through the ESPeasy logs I get '0.00'. Basicially, if some kind soul could take a look at my code and tell me what im doing wrong. What in this code tells ESPeasy to take the PHValue.

Here is the DFRobot sample code for the PH Probe

Code: Select all

/*
 # This sample code is used to test the pH meter V1.1.
 # Editor : YouYou
 # Date   : 2014.06.23
 # Ver    : 1.1
 # Product: analog pH meter V1.1
 # SKU    : SEN0161
*/
#define SensorPin A2            //pH meter Analog output to Arduino Analog Input 2
#define Offset 0.00            //deviation compensate
#define LED 13
#define samplingInterval 20
#define printInterval 800
#define ArrayLenth  40    //times of collection
int pHArray[ArrayLenth];   //Store the average value of the sensor feedback
int pHArrayIndex=0;    
void setup(void)
{
  pinMode(LED,OUTPUT);  
  Serial.begin(9600);  
  Serial.println("pH meter experiment!");    //Test the serial monitor
}
void loop(void)
{
  static unsigned long samplingTime = millis();
  static unsigned long printTime = millis();
  static float pHValue,voltage;
  if(millis()-samplingTime > samplingInterval)
  {
      pHArray[pHArrayIndex++]=analogRead(SensorPin);
      if(pHArrayIndex==ArrayLenth)pHArrayIndex=0;
      voltage = avergearray(pHArray, ArrayLenth)*5.0/1024;
      pHValue = 3.5*voltage+Offset;
      samplingTime=millis();
  }
  if(millis() - printTime > printInterval)   //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
  {
	Serial.print("Voltage:");
        Serial.print(voltage,2);
        Serial.print("    pH value: ");
	Serial.println(pHValue,2);
        digitalWrite(LED,digitalRead(LED)^1);
        printTime=millis();
  }
}
double avergearray(int* arr, int number){
  int i;
  int max,min;
  double avg;
  long amount=0;
  if(number<=0){
    Serial.println("Error number for the array to avraging!/n");
    return 0;
  }
  if(number<5){   //less than 5, calculated directly statistics
    for(i=0;i<number;i++){
      amount+=arr[i];
    }
    avg = amount/number;
    return avg;
  }else{
    if(arr[0]<arr[1]){
      min = arr[0];max=arr[1];
    }
    else{
      min=arr[1];max=arr[0];
    }
    for(i=2;i<number;i++){
      if(arr[i]<min){
        amount+=min;        //arr<min
        min=arr[i];
      }else {
        if(arr[i]>max){
          amount+=max;    //arr>max
          max=arr[i];
        }else{
          amount+=arr[i]; //min<=arr<=max
        }
      }//if
    }//for
    avg = (double)amount/(number-2);
  }//if
  return avg;
}

Here is the code I hacked together to try and get the pHValue to show up in the espeasy logs. I gutted much of the proMiniExtender code down to just what sends data over(or at least I think I did) just to try and figure out why I cant get anything but 0.00

Code: Select all

#include <Wire.h>
  
#define I2C_MSG_IN_SIZE    4
#define I2C_MSG_OUT_SIZE   4


#define CMD_ANALOG_READ    4
#define SensorPin A0            //pH meter Analog output to Arduino Analog Input 2
#define Offset 0.00            //deviation compensate
#define LED 13
#define samplingInterval 20
#define printInterval 800
#define ArrayLenth  40    
//#define SensorPin A2            //pH meter Analog output to Arduino Analog Input 2
 //times of collection

volatile uint8_t sendBuffer[I2C_MSG_OUT_SIZE];
int pHValue; 

int pHArray[ArrayLenth];   //Store the average value of the sensor feedback
int pHArrayIndex=0; 
   
void setup(void)
{
  Wire.begin(0x7f);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
}
int valueRead; 
void loop(void)
{
  static unsigned long samplingTime = millis();
  static unsigned long printTime = millis();
  static float pHValue,voltage;
  if(millis()-samplingTime > samplingInterval)
  {
      pHArray[pHArrayIndex++]=analogRead(SensorPin);
      if(pHArrayIndex==ArrayLenth)pHArrayIndex=0;
      voltage = avergearray(pHArray, ArrayLenth)*5.0/1024;
      pHValue = (3.5*voltage+Offset)*1000;         /////////// // conversion of float Ampere into integer milliAmpere needed for I2C communication
      samplingTime=millis();
      
  }
  if(millis() - printTime > printInterval)   //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
  {
  Serial.print("Voltage:");
        Serial.print(voltage,2);
        Serial.print("    pH value: ");
  Serial.println(pHValue,2);
        digitalWrite(LED,digitalRead(LED)^1);
        printTime=millis();
  }
}
double avergearray(int* arr, int number){
  int i;
  int max,min;
  double avg;
  long amount=0;
  if(number<=0){
    Serial.println("Error number for the array to avraging!/n");
    return 0;
  }
  if(number<5){   //less than 5, calculated directly statistics
    for(i=0;i<number;i++){
      amount+=arr[i];
    }
    avg = amount/number;
    return avg;
  }else{
    if(arr[0]<arr[1]){
      min = arr[0];max=arr[1];
    }
    else{
      min=arr[1];max=arr[0];
    }
    for(i=2;i<number;i++){
      if(arr[i]<min){
        amount+=min;        //arr<min
        min=arr[i];
      }else {
        if(arr[i]>max){
          amount+=max;    //arr>max
          max=arr[i];
        }else{
          amount+=arr[i]; //min<=arr<=max
        }
      }//if
    }//for
    avg = (double)amount/(number-2);
  }//if
  return avg;
}

void receiveEvent(int count)
{
  if (count == I2C_MSG_IN_SIZE)
  {
    byte cmd = Wire.read();
    byte port = Wire.read();
    int value = Wire.read();
    value += Wire.read()*256;
    switch(cmd)
      {
        
        case CMD_ANALOG_READ:
          clearSendBuffer();
          //if (port <= 4) valueRead = analogRead(port);    /////////change this line:[  int valueRead = analogRead(port); ] into: int valueRead = prepared_temperature;
          if (port == 100) valueRead = pHValue;
          sendBuffer[0] = valueRead & 0xff;
          sendBuffer[1] = valueRead >> 8;
          break;
      }
  }
}

void clearSendBuffer()
{
  for(byte x=0; x < sizeof(sendBuffer); x++)
    sendBuffer[x]=0;
}

void requestEvent()
{
  Wire.write((const uint8_t*)sendBuffer,sizeof(sendBuffer));
}
Since the PH probe(like all sensors) gets readings from changes in voltages I tried some code posted here on the board that works for a similar situation. I do get readings from this code to show up in the espeasy logs, although the math is wrong, hence im not getting PH, It Does Work!
costo wrote:

Code: Select all

/********************************************************************************************************************\
 * Arduino project "ESP Easy" Copyright www.esp8266.nu
 *
 * This program 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.
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * You received a copy of the GNU General Public License along with this program in file 'License.txt'.
 *
 * IDE download    : https://www.arduino.cc/en/Main/Software
 * ESP8266 Package : https://github.com/esp8266/Arduino
 *
 * Source Code     : https://sourceforge.net/projects/espeasy/
 * Support         : http://www.esp8266.nu
 * Discussion      : http://www.esp8266.nu/forum/
 *
 * Additional information about licensing can be found at : http://www.gnu.org/licenses
\******************************************************************************************************************/
// This file is to be loaded onto an Arduino Pro Mini so it will act as a simple IO extender to the ESP module.
// Communication between ESP and Arduino is using the I2C bus, so only two wires needed.
// It is possible to run the Pro Mini on 3V3, although the 16MHz versions do not officially support this at 16MHzl 
// By working on 3.3volt  you can skip levelconverters on I2C, but no guarantee that it will work stable.
// Arduino Mini Pro uses A4 and A5 for I2C bus. ESP I2C can be configured but they are on GPIO-4 and GPIO-5 by default.

#include <Wire.h>

#define I2C_MSG_IN_SIZE    4
#define I2C_MSG_OUT_SIZE   4

#define CMD_DIGITAL_WRITE  1
#define CMD_DIGITAL_READ   2
#define CMD_ANALOG_WRITE   3
#define CMD_ANALOG_READ    4

volatile uint8_t sendBuffer[I2C_MSG_OUT_SIZE];
int ESPrms;                                                          // value to be sent over I2C to ESP

void setup()
{
  Wire.begin(0x7f);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
}


/* ******************************************************************************************************************
 This part in the loop found here: [url] https://forum.arduino.cc/index.php?topic=179541.0 [/url] tnx to dc42
 constant 75.7576 depends on the sensitivity of the ACS712 module
 Sensitivity Min<Typ<Max mV/A
 for 30A:     64< 66< 68 mV/A > constant = 5/.066 = 75.76
 for 10A:     96<100<104 mV/A > constant = 5/.1   = 50.00
 for 5A:     180<185<190 mV/A > constant = 5/.185 = 27.03
 */


const int currentPin = A7;                                  // ADC pin used for ACS712
const unsigned long sampleTime = 100000UL;    // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains
const unsigned long numSamples = 250L;          // number of samples 400 microsecond each
const unsigned long sampleInterval = sampleTime/numSamples; // sampling interval, must be longer than then ADC conversion time
int adc_zero = 514;                                          // relative zero of the ACS712 for me 514 was best.  511 
int valueRead;                                                 // value read on ADC

void loop()
{
 unsigned long currentAcc = 0;
 unsigned int count = 0;
 unsigned long prevMicros = micros() - sampleInterval ;

 while (count < numSamples)
 {
   if (micros() - prevMicros >= sampleInterval)
   {
     long adc_raw = analogRead(currentPin) - adc_zero;
     currentAcc += (unsigned long)(adc_raw * adc_raw);
     ++count;
     prevMicros += sampleInterval;
   }
 }
 
 float rms = sqrt((float)currentAcc/(float)numSamples) * (27.03 / 1024.0);  // see note above for this 27.03 value 
  ESPrms = 1000*rms;            // conversion of float Ampere into integer milliAmpere needed for I2C communication
}
/* *******************************************************************************************************************

void receiveEvent(int count)
{
  if (count == I2C_MSG_IN_SIZE)
  {
    byte cmd = Wire.read();
    byte port = Wire.read();
    int value = Wire.read();
    value += Wire.read()*256;
    switch(cmd)
      {
        case CMD_DIGITAL_WRITE:
          pinMode(port,OUTPUT);
          digitalWrite(port,value);
          break;
        case CMD_DIGITAL_READ:
          pinMode(port,INPUT_PULLUP);
          clearSendBuffer();
          sendBuffer[0] = digitalRead(port);
          break;
        case CMD_ANALOG_WRITE:
          analogWrite(port,value);
          break;
        case CMD_ANALOG_READ:
          clearSendBuffer();
          if (port <= 4) valueRead = analogRead(port);  // port <=4 to read analog value A0,A1,A2,A3 - A4 & A5 are I2C
          if (port == 100) valueRead = ESPrms;           // port is any number >4 up to 255
          sendBuffer[0] = valueRead & 0xff;
          sendBuffer[1] = valueRead >> 8;
          break;
      }
  }
}

void clearSendBuffer()
{
  for(byte x=0; x < sizeof(sendBuffer); x++)
    sendBuffer[x]=0;
}

void requestEvent()
{
  Wire.write((const uint8_t*)sendBuffer,sizeof(sendBuffer));
}

In the sketch for adc_zero = 514 was for my case the best value. Theoretically it should be 511. You should experiment with this value to get the lowest reading without current. It will never be zero because the ACS712 has some noise from itself. About 0.12 Ampere was the lowest zero reading I got.

In the device on ESPEasy:
Device: ProMini Extender
Name: Ampere
IDX: some value needed here, ID number from the server
Port: 100 (port number choosen in the sketch to couple with rms_value)
Port Type: Analog

Formula Value: %value%/1000 (to convert milliAmp to Ampere
Here is my version of the code that works but alas, these are not PH Values..

Code: Select all

/********************************************************************************************************************\
 * Arduino project "ESP Easy" Copyright www.esp8266.nu
 *
 * This program 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.
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * You received a copy of the GNU General Public License along with this program in file 'License.txt'.
 *
 * IDE download    : https://www.arduino.cc/en/Main/Software
 * ESP8266 Package : https://github.com/esp8266/Arduino
 *
 * Source Code     : https://sourceforge.net/projects/espeasy/
 * Support         : http://www.esp8266.nu
 * Discussion      : http://www.esp8266.nu/forum/
 *
 * Additional information about licensing can be found at : http://www.gnu.org/licenses
\******************************************************************************************************************/
// This file is to be loaded onto an Arduino Pro Mini so it will act as a simple IO extender to the ESP module.
// Communication between ESP and Arduino is using the I2C bus, so only two wires needed.
// It is possible to run the Pro Mini on 3V3, although the 16MHz versions do not officially support this at 16MHzl
// By working on 3.3volt  you can skip levelconverters on I2C, but no guarantee that it will work stable.
// Arduino Mini Pro uses A4 and A5 for I2C bus. ESP I2C can be configured but they are on GPIO-4 and GPIO-5 by default.

#include <Wire.h>

#define I2C_MSG_IN_SIZE    4
#define I2C_MSG_OUT_SIZE   4

#define CMD_DIGITAL_WRITE  1
#define CMD_DIGITAL_READ   2
#define CMD_ANALOG_WRITE   3
#define CMD_ANALOG_READ    4

volatile uint8_t sendBuffer[I2C_MSG_OUT_SIZE];
int ESPrms;                                                          // value to be sent over I2C to ESP
//int rms;                                                          // value to be sent over I2C to ESP

void setup()
{
  Wire.begin(0x7f);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
}


/* ******************************************************************************************************************
 This part in the loop found here: [url] https://forum.arduino.cc/index.php?topic=179541.0 [/url] tnx to dc42
 constant 75.7576 depends on the sensitivity of the ACS712 module
 Sensitivity Min<Typ<Max mV/A
 for 30A:     64< 66< 68 mV/A > constant = 5/.066 = 75.76
 for 10A:     96<100<104 mV/A > constant = 5/.1   = 50.00
 for 5A:     180<185<190 mV/A > constant = 5/.185 = 27.03
 */


const int currentPin = A0;                                  // ADC pin used for ACS712
const unsigned long sampleTime = 100000UL;    // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains
const unsigned long numSamples = 10000;          // number of samples 400 microsecond each
const unsigned long sampleInterval = sampleTime/numSamples; // sampling interval, must be longer than then ADC conversion time
int adc_zero = 514;                                          // relative zero of the ACS712 for me 514 was best.  511
int valueRead;                                                 // value read on ADC

void loop()
{
 unsigned long currentAcc = 0;
 unsigned int count = 0;
 unsigned long prevMicros = micros() - sampleInterval ;

 while (count < numSamples)
 {
   if (micros() - prevMicros >= sampleInterval)
   {
     long adc_raw = analogRead(currentPin) - adc_zero;
     currentAcc += (unsigned long)(adc_raw * adc_raw);
     ++count;
     prevMicros += sampleInterval;
   }
 }
 
 float rms = sqrt((float)currentAcc/(float)numSamples) * (27.03 / 1024.0);  // see note above for this 27.03 value
  ESPrms = 2000*rms;            // conversion of float Ampere into integer milliAmpere needed for I2C communication
}
//* *******************************************************************************************************************

void receiveEvent(int count)
{
  if (count == I2C_MSG_IN_SIZE)
  {
    byte cmd = Wire.read();
    byte port = Wire.read();
    int value = Wire.read();
    value += Wire.read()*256;
    switch(cmd)
      {
        
        case CMD_ANALOG_READ:
          clearSendBuffer();
          //if (port <= 0) valueRead = analogRead(port);  // port <=4 to read analog value A0,A1,A2,A3 - A4 & A5 are I2C
          if (port == 100) valueRead = ESPrms;           // port is any number >4 up to 255
          //sendBuffer[0] = valueRead & 0xff;
          sendBuffer[1] = valueRead >> 8;
          break;
      }
  }
}

void clearSendBuffer()
{
  for(byte x=0; x < sizeof(sendBuffer); x++)
    sendBuffer[x]=0;
}

void requestEvent()
{
  Wire.write((const uint8_t*)sendBuffer,sizeof(sendBuffer));
}


sel303
New user
Posts: 8
Joined: 19 Oct 2016, 12:09

Re: DFRobot PH sensor > Arduino > ESP8266 > MQTT

#2 Post by sel303 » 29 Oct 2016, 18:20

I get the Extender code to send values with the PH probe, its just not with the correct code, so the values are obviously wrong. I just wanted to make sure I emphasized that. I i2c scanner recognizes both versions, I just cant get values from the code I actually need.lol

Post Reply

Who is online

Users browsing this forum: No registered users and 32 guests