pH/ORP stamp Atlas scientific

Hi,

I'm working on an automated hydroponic system that reads pH levels as well as PPM.

Basically, I want the arduino to read the levels every 5 sec and do adjustments:

Set pH level = 7.0
Set EC level = 1000 ppm

if actual pH < 7.0, activate device x
same with ppm

I am using the Atlas scientific pH 3.0 and the EC 2.0
https://www.atlas-scientific.com/embedded.html
https://www.atlas-scientific.com/_files/pH_Circuit_3.0.pdf
https://www.atlas-scientific.com/product_pages/embedded/ec-2.html

they provide a sample code
https://www.atlas-scientific.com/_files/code/Arduino-sample-code-EZ-COM.pdf

but I find it super complicated (since I'm a complete noob)

with this code, I'm getting a continuous serial read, but I can't seem to use the values to run a comparaison with my preset values...

can anyone help?

Thanks

This relates to this thread but is an other subject - http://arduino.cc/forum/index.php/topic,98529.msg739052.html -

with this code,

This code? What code?

I'm getting a continuous serial read, but I can't seem to use the values to run a comparaison with my preset values...

Why not? It doesn't matter how often you get a value.

That sensor, by the way, can be configured to only provide a value when you request one.

Thanks for your responses,

robtillaart:
This relates to this thread but is an other subject - pH monitoring with Atlas Scientific pH stamp - Home Automation - Arduino Forum -

Yes it does, but I found in the old forum something very similar today under the category "Interfacing w/ Software on the Computer", so I just wanted to put this in the right place.

as a matter of fact, PaulS, you helped a lot in that post:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294830203

This code? What code?

#include <SoftwareSerial.h>

#define rxpin 2 
#define txpin 3


SoftwareSerial myserial (rxpin, txpin);
String inputstring = "";
String sensorstring = "";
boolean input_stringcomplete = false;
boolean sensor_stringcomplete = false;
float pH_val = 0.0;

void setup(){ 
Serial.begin(38400);
myserial.begin(38400);
inputstring.reserve(5);
sensorstring.reserve(30);
}  

void serialEvent() { 
char inchar = (char)Serial.read();
inputstring += inchar;
if(inchar == '\r') {input_stringcomplete = true;}
pH_val = atof(inch);
}       

 void loop(){
if (input_stringcomplete){
   myserial.print(inputstring); 
   inputstring = "";
   input_stringcomplete = false;
}                 

while (myserial.available()) {
char inchar = (char)myserial.read();
sensorstring += inchar;
if (inchar == '\r') {sensor_stringcomplete = true;}
}
 if (sensor_stringcomplete){
   Serial.print(sensorstring);
   sensorstring = ""; 
   sensor_stringcomplete = false;
   }

}

robtillaart also helped me a lot and got this more straightforward code to work

#include <SoftwareSerial.h>

#define rxph 2 
#define txph 3

SoftwareSerial pH (rxph, txph);

void setup()
{ 
  Serial.begin(38400);
  pH.begin(38400);
}  

 void loop()
{
  if (pH.available() >0) 
  {
    char incharPH = (char)pH.read();
    Serial.print(incharPH);
    
  }
}

but I am still having calibration issues: stamp is supposed to read pH7 in a test solution but only reads 6.8 and then in another test solution of pH 10, it reads 12...
According to the datasheet, I should be able to send some commands to be able to calibrate the stamp, but I have yet to succeed...

I'm getting a continuous serial read, but I can't seem to use the values to run a comparaison with my preset values...
Why not? It doesn't matter how often you get a value.

That sensor, by the way, can be configured to only provide a value when you request one.

I'm really not sure how to do that... :~

thanks a lot for your help and sorry for the missunderstanding

String inputstring = "";
String sensorstring = "";
boolean input_stringcomplete = false;
boolean sensor_stringcomplete = false;
float pH_val = 0.0;

inputstring.reserve(5);
sensorstring.reserve(30);

Whenever I see code like this, I cringe. If you know how much data you are expecting to send or receive, use a fixed size char array, not the overhead of a String.

void serialEvent() { 
char inchar = (char)Serial.read();
inputstring += inchar;
if(inchar == '\r') {input_stringcomplete = true;}
pH_val = atof(inch);
}

I don't see inch defined anywhere. It seems to be that you only want to convert the float to a value when the end of the string is encountered, not every time this function is called.

It is not clear what is sending data to the hardware serial port and what is sending data to the software serial port.

According to the datasheet, I should be able to send some commands to be able to calibrate the stamp, but I have yet to succeed...

My crystal ball is at the cleaners.

Whenever I see code like this, I cringe. If you know how much data you are expecting to send or receive, use a fixed size char array, not the overhead of a String.

so do you mean I should do something more like this code?

}
void loop(void)
{

char inData_ORP[12];
char string_ORP[8];

this snippet comes from this code I found online:

#include <NewSoftSerial.h>

NewSoftSerial LCD(2, 3); // LCD
NewSoftSerial ORP(4, 5); // ORP stamp
NewSoftSerial pH(6, 7);

int ORP_val = 0;
float pH_val = 0.0;

void setup()
{
Serial.begin(9600);
LCD.begin(9600); // LCD
ORP.begin(9600); // ORP stamp
pH.begin(9600); // pH stamp

LCD.print("?f");
LCD.print("?c0");

}

void loop(void)
{

char inData_ORP[12];
char string_ORP[8];

ORP.println("read()c");
delay(750);


if (ORP.available() > 0)
{
for (int i=0;i<13;i++) {
inData_ORP[i] = char(ORP.read());
}
}
sscanf(inData_ORP, "%*s %s ", string_ORP);
ORP.flush();
if (atoi(string_ORP) != 0) {
ORP_val = atoi(string_ORP);
}

Serial.print("Sensor output: [");
Serial.print(inData_ORP);
Serial.println("]");
LCD.print("?y2");
LCD.print("?x00");
LCD.print("ORP value: ");
LCD.println(ORP_val);

char inData_pH[12];
char string_pH[8];

pH.println("read(26.5)c");
delay(750);

if (pH.available() > 0)
{
for (int i=0;i<13;i++) {
inData_pH[i] = char(pH.read());
}
}
sscanf(inData_pH, "%*3c%s ", string_pH);
pH.flush();
if (atoi(string_pH) != 0) {
pH_val = atof(string_pH);
}

Serial.print("Sensor output: [");
Serial.print(inData_pH);
Serial.println("]");
LCD.print("?y0");
LCD.print("?x00");
LCD.print("pH value: ");
LCD.println(pH_val);

}

Unfortunately, this code prints out for the pH value something like this: [3.863.863.8] and spits out from time to time random characters during the readout on the serial monitor...

I don't see inch defined anywhere. It seems to be that you only want to convert the float to a value when the end of the string is encountered, not every time this function is called.

My bad, I pasted the wrong code... that was me trying to play around with atof (obvious failure there...)
here is the original code provided by Atlas Scientific

#include <SoftwareSerial.h>  //add the soft serial libray

#define rxpin 2 //set the RX pin to pin 2
#define txpin 3 //set the TX pin to pin 3


SoftwareSerial myserial (rxpin, txpin); //enable the soft serial port
String inputstring = ""; //a string to hold incoming data from the PC
String sensorstring = ""; //a string to hold the data from the Atlas Scientific product
boolean input_stringcomplete = false; //have we received all the data from the PC
boolean sensor_stringcomplete = false; //have we received all the data from the Atlas Scientific 
//product

void setup(){ //set up the hardware
Serial.begin(38400); //set baud rate for the hardware serial port to 38400
myserial.begin(38400); //set baud rate for software serial port to 38400
inputstring.reserve(5); //set aside some bytes for receiving data from the PC
sensorstring.reserve(30); //set aside some bytes for receiving data from the Atlas Scientific product
}  

void serialEvent() { //if the hardware serial port receives a char
char inchar = (char)Serial.read(); //get the char we just received
inputstring += inchar; //add it to the inputString
if(inchar == '\r') {input_stringcomplete = true;} //if the incoming character is a <CR>, set the flag
}       

 void loop(){ //here we go...

if (input_stringcomplete){ //if a string from the PC has been recived in its entiery
   myserial.print(inputstring); //send that string to the Atlas Scientific product
   inputstring = ""; //clear the string:
   input_stringcomplete = false; //reset the flage used to tell if we have recived a comp[lete string from the PC
}                 

while (myserial.available()) { //while a char is holding in the serial buffer
char inchar = (char)myserial.read(); //get the new char
sensorstring += inchar; //add it to the sensorstring
if (inchar == '\r') {sensor_stringcomplete = true;} //if the incoming character is a <CR>, set the flag

}
 if (sensor_stringcomplete){ //if a string from the Atlas Scientific product has been received in its entirety
   Serial.print(sensorstring); //use the hardware serial port to send that data to the PC
   sensorstring = ""; //clear the string:
   sensor_stringcomplete = false; //reset the flag used to tell if we have received a completed string from the Atlas Scientific product

   }

}

It is not clear what is sending data to the hardware serial port and what is sending data to the software serial port.

Honestly, I am completely lost... I have been reading books about arduino, but the ones at my level of understanding don't get even close to this kind of code...

I m just good at blinking LEDs in many ways :stuck_out_tongue:

so do you mean I should do something more like this code?

Yes. You also need two variables to keep track of where to write in the arrays.

this snippet comes from this code I found online:

Crappy code. Don't use it.

if (ORP.available() > 0)
{
for (int i=0;i<13;i++) {
inData_ORP[i] = char(ORP.read());
}
}

If there is at least one byte available, read all 14 of them. Yeah, right. Great idea.

Unfortunately, this code prints out for the pH value something like this: [3.863.863.8] and spits out from time to time random characters during the readout on the serial monitor...

And, now you know why. At least, I hope you see why.

Honestly, I am completely lost...

You are the one sitting in front of the hardware. Look at it. What pins is the pH probe connected to?

The SoftwareSerial instance should be given a meaningful name. mySerial is not such a name. When the instance is used to read data from pins that a gps, or phone, or modem, or pH probe are connected to, instance names like gps, phone or cell, modem, and pHprobe or probe make a lot more sense. Then, there is no confusing what the instance represents.

There is also no mistakenly doing probe.print() to send data to the serial monitor.

So, you should look at the various pieces of code you have posted, especially the one I was questioning, and see if is clear to you what each .read() and each .print() method is communicating with. If it is, fine. If not, see if using a more meaningful name for the SoftwareSerial instance wouldn't help clear up the confusion. Also, make sure that each .read() is reading from the correct source, and that each .print() is sending data to the correct destination.

It is not clear to me that this is happening in that code.

Hey, sorry got bombarded with work...

yes I am starting to understand, but it is all still quite blurry...

You are the one sitting in front of the hardware. Look at it. What pins is the pH probe connected to?

the pH probe itself is connected to the stamp: GND-->GND, Prb-->Prb.
the Stamp is connected RX-->Pin2, TX-->Pin3, GND-->GND and VCC-->5V.

It is not clear to me that this is happening in that code.

The problem is that I seem to understand what is going on, but at the same time, when I try to do any slight modification (obviously apart from renaming "myserial" to something more relevant) I hit a brick wall...

Isn't there an easier way to read the sensors and have that value stored as "new val" so that the arduino can run an "if" statement?

The problem is that I seem to understand what is going on, but at the same time, when I try to do any slight modification (obviously apart from renaming "myserial" to something more relevant) I hit a brick wall...

Every time you hit a brick wall, and need help, you need to post your latest code.

Isn't there an easier way to read the sensors and have that value stored as "new val" so that the arduino can run an "if" statement?

It really isn't that hard to only read the serial data that is present, and wait to act on the data until all the data has arrived.

Every time you hit a brick wall, and need help, you need to post your latest code.

I went back to the original manufacturer's code because now it works perfectly with both of my pH and ECC probes (for now I only took care of the pH part):

#include <SoftwareSerial.h>  //add the soft serial libray

#define rxph 2 //set the RX pin to pin 2
#define txph 3 //set the TX pin to pin 3



SoftwareSerial pH (rxph, txph); //enable the soft serial port
String inputstringPH = ""; //a string to hold incoming data from the PC
String sensorstringPH = ""; //a string to hold the data from the Atlas Scientific product
boolean inputPH_stringcomplete = false; //have we received all the data from the PC
boolean sensorPH_stringcomplete = false; //have we received all the data from the Atlas Scientific 
//product



void setup(){ //set up the hardware
Serial.begin(38400); //set baud rate for the hardware serial port to 38400
pH.begin(38400); //set baud rate for software serial port to 38400
inputstringPH.reserve(5); //set aside some bytes for receiving data from the PC
sensorstringPH.reserve(30); //set aside some bytes for receiving data from the Atlas Scientific product

}  

void serialEvent() { //if the hardware serial port receives a char
char incharPH = (char)Serial.read(); //get the char we just received
inputstringPH += incharPH; //add it to the inputStringPH
if(incharPH == '\r') {inputPH_stringcomplete = true;} //if the incoming character is a <CR>, set the flag
}       

 void loop(){ //here we go...

if (inputPH_stringcomplete){ //if a string from the PC has been recived in its entiery
   pH.print(inputstringPH); //send that string to the Atlas Scientific product
   inputstringPH = ""; //clear the string:
   inputPH_stringcomplete = false; //reset the flage used to tell if we have recived a comp[lete string from the PC
}                 

while (pH.available()) { //while a char is holding in the serial buffer
char incharPH = (char)pH.read(); //get the new char
sensorstringPH += incharPH; //add it to the sensorstring
if (incharPH == '\r') {sensorPH_stringcomplete = true;} //if the incoming character is a <CR>, set the flag

}
 if (sensorPH_stringcomplete){ //if a string from the Atlas Scientific product has been received in its entirety
   Serial.println(sensorstringPH); //use the hardware serial port to send that data to the PC
   sensorstringPH = ""; //clear the string:
   sensorPH_stringcomplete = false; //reset the flag used to tell if we have received a completed string from the Atlas Scientific product
//pH_val=atof(sensorstringPH); //convert the pH string to a float
   }

}

Hi there,

Im doing the exact same thing as aekhalil using both the same sensors with the provided code as aelkhalil posted but I too am having issues converting the strings into floats which I can use to compare against a set value.

For the pH sensor I understand I need to use the atof function however I get the error : cannot convert 'String' to 'const char*' for argument '1' to 'double atof(const char*)' if I simply add:

  if (sensorPH_stringcomplete)  //if a string from the Atlas Scientific product has been received in its entirety
 { 
  Serial.println(sensorstringPH); //use the hardware serial port to send that data to the PC
  sensorstringPH = ""; //clear the string:    //show this
  sensorPH_stringcomplete = false; //reset the flag used to tell if we have received a completed string from the Atlas Scientific product
  pH_reading = atof(sensorstringPH);
  Serial.println(pH_reading);
 }
}

For the EC probe the output is a string consisting of three comma seperated values (5000, 32800, 32.7). I only want use the first value in this instance and realise I need to split the string using strtok? This is the code I am using for a basis and it does as I wish:

#include <string.h>

char sz[] = "Here; is some; sample;100;data;1.414;1020";
void setup()
{
 char *p = sz;
 char *str;
 Serial.begin(9600);
 while ((str = strtok_r(p, ";", &p)) != NULL) // delimiter is the semicolon
   Serial.println(str);
}

void loop(){}

I have inserted into this part of my EC code which is similar to posted above:

 if (sensorEC_stringcomplete)  //if a string from the Atlas Scientific product has been received in its entirety
 { 
//  Serial.println(sensorstringEC); //use the hardware serial port to send that data to the PC
  char *str;
  while ((str = strtok_r(sensorstringEC, ",", &sensorstringEC)) != NULL) // delimiter is the semicolon
  Serial.println(str);
  sensorstringEC = ""; //clear the string:    //show this
  sensorEC_stringcomplete = false; //reset the flag used to tell if we have received a completed string from the Atlas Scientific product
 }

but retrieve the error: 'String' to 'char*' for argument '1' to 'char* strtok_r(char*, const char*, char**)'

I haven't done anything like this in coding before, could someone please clarify whats going on.

Thanks

Is there some part of the error message(s) that you don't understand. Ditch the stupid String class!

Right Ok, so I have got rid of the pH problem I was having here is how I solved it:

if (sensorPH_stringcomplete)  //if a string from the Atlas Scientific product has been received in its entirety
 { 
   char buf[sensorstringPH.length()];
   sensorstringPH.toCharArray(buf, sensorstringPH.length());
   float pH_reading = atof(buf);
   float check = pH_reading*10;
   Serial.println(check);

As for EC problem I have found out that I can split the string using:

#include <string.h>

char EC_reading;

String Filename = "50000,3720,42.5";
void setup()
{
 Serial.begin(9600);
   char buf[Filename.length()];
  Filename.toCharArray(buf,Filename.length());
 char *p = buf;
 char *str;
  while ((str = strtok_r(p, ",", &p)) != NULL) // delimiter is the comma
  Serial.println(str);
}

void loop(){}

however two problems:

  1. Why does this miss out the last number from Filename (ie the 5)? Not a big issue as I only want to use the first value but still dont understand why it does this. If I put a delimiter at the end of my string it will pick up everything
  2. What does str become? Is it a character array? Im having difficulty extracting just the '5000' before I then will convert it to a float for use elsewhere in my code

The strtok_r() function if the thread_safe version of strtok(). It is far more complex to use, and las a much larger code footprint. For a single threaded system, why are you using strtok_r() instead of strtok()?

String Filename = "50000,3720,42.5";

What kind of file name is that?

   char buf[Filename.length()];
  Filename.toCharArray(buf,Filename.length());

Why are you insisting on wrapping the character data in a String when you have to extract it to use it?

  1. Why does this miss out the last number from Filename (ie the 5)?

Because you are missing a \0 as a delimiter.

  1. What does str become? Is it a character array?

It doesn't become anything. It is, and always will be a pointer to a character. For the most part, it is treated as an array.

Im having difficulty extracting just the '5000' before I then will convert it to a float for use elsewhere in my code

Does that have anything to do with the fact that you overwrite str in the while loop? 50000 is not a float. It is an integer (technically, a long int). Why do you want to convert it to a float?

Thanks PaulS,

Firstly I must say I'm very unfamiliar with all this so not entirely sure what's going on here.

50000 is not a float. It is an integer (technically, a long int). Why do you want to convert it to a float?

Yes I realise that, the code I uploaded was just a test to see if I could get things to do what I wanted, what actually will happen is every reading I receive from my EC sensor will be three values comma separated giving me information on EC, total dissolved salts (TDS) and parts per million(PPM), these values wont necessarily be integers. I just want to try and split that string, take the first value and convert it into something (a float) I can use elsewhere in my code. The reason I used strtok_r instead of strtok came from this thread I read: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231961642, I just changed it to strtok and got the following error:

function 'char* strtok(char*, const char*)'
sketch_apr21b:12: error: at this point in file

Whats buf? If I do:

#include <Wire.h>
#include <EasyTransferI2C.h>

//create object
EasyTransferI2C ET; 

struct SEND_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int blinks;
  int pause;
};

//give a name to the group of data
SEND_DATA_STRUCTURE mydata;

//define slave i2c address
#define I2C_SLAVE_ADDRESS 9

void setup(){
  Wire.begin();
  //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
  ET.begin(details(mydata), &Wire);
  
  pinMode(13, OUTPUT);
  
  randomSeed(analogRead(0));
  
}

void loop(){
  //this is how you access the variables. [name of the group].[variable name]
  mydata.blinks = random(5);
  mydata.pause = random(5);
  //send the data
  ET.sendData(I2C_SLAVE_ADDRESS);
  
  //Just for fun, we will blink it out too
   for(int i = mydata.blinks; i>0; i--){
      digitalWrite(13, HIGH);
      delay(mydata.pause * 100);
      digitalWrite(13, LOW);
      delay(mydata.pause * 100);
    }
  
  delay(5000);
}

I get 48 for EC_reading?

I just changed it to strtok and got the following error:

function 'char* strtok(char*, const char*)'
sketch_apr21b:12: error: at this point in file

I'm almost certain that this is not the error that you got. Post the code, so we can see what you changed the code to.