Atlas Scientific Ardiuno to Particle code issues

Hey Everyone,

I am having an issue using my aduino code on my particle for an atlas scientific pH i2c stamp.

How can i convert this to work on particle Photon, im just lost and need some help.

/*
*THIS CODE WILL WORK ON ANY ARDUINO** 
//This code has intentionally has been written to be overly lengthy and includes unnecessary steps.
//Many parts of this code can be truncated. This code was written to be easy to understand.
//Code efficiency was not considered. Modify this code as you see fit.
//This code will output data to the Arduino serial monitor. Type commands into the Arduino serial monitor to control the EZO pH Circuit in I2C mode.
*/

**#include <Wire.h>                //enable I2C.**
**#define address 99               //default I2C ID number for EZO pH Circuit.**


char computerdata[20];           //we make a 20 byte character array to hold incoming data from a pc/mac/other.   
byte received_from_computer=0;   //we need to know how many characters have been received.    
byte serial_event=0;             //a flag to signal when data has been received from the pc/mac/other. 
byte code=0;                     //used to hold the I2C response code. 
char ph_data[20];                //we make a 20 byte character array to hold incoming data from the pH circuit. 
byte in_char=0;                  //used as a 1 byte buffer to store in bound bytes from the pH Circuit.   
byte i=0;                        //counter used for ph_data array. 
int time_=1800;                   //used to change the delay needed depending on the command sent to the EZO Class pH Circuit. 
float ph_float;                  //float var used to hold the float value of the pH. 


void setup()                    //hardware initialization.                
{
  Serial.begin(9600);           //enable serial port.  
  Wire.begin();                 //enable I2C port.
}

// annotation ScruffR: this is actually NOT an interrupt !!!
void serialEvent()
{ //this interrupt will trigger when the data coming from the serial monitor(pc/mac/other) is received.    
  received_from_computer=Serial.readBytesUntil(13,computerdata,20); //we read the data sent from the serial monitor(pc/mac/other) until we see a <CR>. We also count how many characters have been received.      
  computerdata[received_from_computer]=0;  //stop the buffer from transmitting leftovers or garbage.
  serial_event=1;
}    
        
void loop(){                    //the main loop.

  if(serial_event){            //if the serial_event=1.
        if(computerdata[0]=='c'||computerdata[0]=='r')time_=1800; //if a command has been sent to calibrate or take a reading we wait 1800ms so that the circuit has time to take the reading.  
       else time_=300;         //if any other command has been sent we wait only 300ms.
       
    
    Wire.beginTransmission(address); //call the circuit by its ID number.  
    Wire.write(computerdata);        //transmit the command that was sent through the serial port.
    Wire.endTransmission();          //end the I2C data transmission. 
    
    
    delay(time_);                    //wait the correct amount of time for the circuit to complete its instruction. 
    
    Wire.requestFrom(address,20,1); //call the circuit and request 20 bytes (this may be more than we need)
    code=Wire.read();               //the first byte is the response code, we read this separately.  
    
    switch (code){                  //switch case based on what the response code is.  
      case 1:                       //decimal 1.  
        Serial.println("Success");  //means the command was successful.
      break;                        //exits the switch case.
    
     case 2:                        //decimal 2. 
       Serial.println("Failed");    //means the command has failed.
     break;                         //exits the switch case.
    
     case 254:                      //decimal 254.
       Serial.println("Pending");   //means the command has not yet been finished calculating.
     break;                         //exits the switch case.
     
     case 255:                      //decimal 255.
       Serial.println("No Data");   //means there is no further data to send.
     break;                         //exits the switch case.
    }
    
 
 
 
 
  while(Wire.available()){          //are there bytes to receive.  
   in_char = Wire.read();           //receive a byte.
   ph_data[i]= in_char;             //load this byte into our array.
   i+=1;                            //incur the counter for the array element. 
    if(in_char==0){                 //if we see that we have been sent a null command. 
        i=0;                        //reset the counter i to 0.
        Wire.endTransmission();     //end the I2C data transmission.
        break;                      //exit the while loop.
    }
  }
  
  Serial.println(ph_data);          //print the data.  
  serial_event=0;                   //reset the serial event flag.
  }

  //Uncomment this section if you want to take the pH value and convert it into floating point number.
  //ph_float=atof(ph_data); 
}

Have a look at this first and see how far you get with this
Porting from Arduino to Particle.io (Simplified-Hopefully)

After that we’ll be there to address more complicated matters.

1 Like

Cheers for the help, ill give that a shot. Looks to work. So to recap, essentially particle has just replaced all those commands with a simple application.h? If so thats very simple.

1 Like

Awesome! I’ve been using the Atlas PH, EC, and Temp sensors for a few months without any issues for a hydroponics project. The photon’s been great because it was real easy to stream real-time sensor data to Ubidots which I then embed in my website to remotely monitor my water stats (http://danklettuce.com/real-time-dashboard) . I still do my calibrations for the sensors with a regular Arduino because the serial interface is easy, but after calibration the Photon handles them like a champ.

My test code for the EC sensor is below and you should be able to tweak it pretty easily to work with the PH sensor. Once you’ve got the code tweaked and flashed head over to https://dashboard.particle.io/user/devices in order to make sure that your data is streaming to your particle dashboard. Should display a new reading every 5 seconds but you can tweak that in the code by modifying the line “delay(5000-delay_time)”.

//**THIS CODE WILL WORK ON ANY ARDUINO**
//This code has intentionally has been written to be overly lengthy and includes unnecessary steps.
//Many parts of this code can be truncated. This code was written to be easy to understand.
//Code efficiency was not considered. Modify this code as you see fit.
//This code will output data to the Arduino serial monitor. Type commands into the Arduino serial monitor to control the EZO EC Circuit in I2C mode.


//#include <Wire.h>                //enable I2C.
#define address 100              //default I2C ID number for EZO EC Circuit.



char computerdata[20];           //we make a 20 byte character array to hold incoming data from a pc/mac/other.
byte received_from_computer = 0; //we need to know how many characters have been received.
bool serial_event = false;       //a flag to signal when data has been received from the pc/mac/other.
byte code = 0;                   //used to hold the I2C response code.
char ec_data[48];                //we make a 48 byte character array to hold incoming data from the EC circuit.
byte in_char = 0;                //used as a 1 byte buffer to store in bound bytes from the EC Circuit.
byte i = 0;                      //counter used for ec_data array.
int delay_time = 1400;           //used to change the delay needed depending on the command sent to the EZO Class EC Circuit.

char *ec;                        //char pointer used in string parsing.
char *tds;                       //char pointer used in string parsing.
char *sal;                       //char pointer used in string parsing.
char *sg;                        //char pointer used in string parsing.

float ec_float;                  //float var used to hold the float value of the conductivity.
float tds_float;                 //float var used to hold the float value of the TDS.
float sal_float;                 //float var used to hold the float value of the salinity.
float sg_float;                  //float var used to hold the float value of the specific gravity.


void setup()                     //hardware initialization.
{
  Serial.begin(9600);            //enable serial port.
  Wire.begin();                  //enable I2C port.
}


void serialEvent() {           //this interrupt will trigger when the data coming from the serial monitor(pc/mac/other) is received.
  received_from_computer = Serial.readBytesUntil(13, computerdata, 20); //we read the data sent from the serial monitor(pc/mac/other) until we see a <CR>. We also count how many characters have been received.
  computerdata[received_from_computer] = 0; //stop the buffer from transmitting leftovers or garbage.
  serial_event = true;           //set the serial event flag.
}

void loop() {                   //the main loop will take a reading every 5 seconds.

    computerdata[0] = 'r'; //Take a reading
    delay_time=1800; // Need to set the delay to 1.8 seconds when taking a reading

    Wire.beginTransmission(address);            //call the circuit by its ID number.
    Wire.write(computerdata);                   //transmit the command that was sent through the serial port.
    Wire.endTransmission();                     //end the I2C data transmission.

    delay(delay_time);                        //wait the correct amount of time for the circuit to complete its instruction.

    Wire.requestFrom(address, 48, 1);          //call the circuit and request 48 bytes (this is more then we need).
    code = Wire.read();                        //the first byte is the response code, we read this separately.
    
    while (Wire.available()) {                  //are there bytes to receive.
      in_char = Wire.read();                    //receive a byte.
      ec_data[i] = in_char;                     //load this byte into our array.
      i += 1;                                   //incur the counter for the array element.
      if (in_char == 0) {                       //if we see that we have been sent a null command.
        i = 0;                                  //reset the counter i to 0.
        Wire.endTransmission();                 //end the I2C data transmission.
        break;                                  //exit the while loop.
      }
    }
       
    switch (code) {                           //switch case based on what the response code is.
      case 1:                                 //decimal 1.
        Serial.println("Success");            //means the command was successful.
        break;                                //exits the switch case.

      case 2:                                 //decimal 2.
        Serial.println("Failed");             //means the command has failed.
        break;                                //exits the switch case.

      case 254:                               //decimal 254.
        Serial.println("Pending");            //means the command has not yet been finished calculating.
        break;                                //exits the switch case.

      case 255:                               //decimal 255.
        Serial.println("No Data");            //means there is no further data to send.
        break;                                //exits the switch case.
    }
  
//    Serial.println(ec_data);                  //print the data.
    serial_event = false;                      //reset the serial event flag.

    if(computerdata[0]=='r') string_pars(); //Call the Strign_Pars() function to break up the comma separated ec_data string into its individual parts.
    
    delay(5000-delay_time); //this will pause until 5 seconds have elapsed from previous reading
  }

void string_pars() {                  //this function will break up the CSV string into its 4 individual parts. EC|TDS|SAL|SG.
                                      //this is done using the C command “strtok”.

  ec = strtok(ec_data, ",");          //let's pars the string at each comma.
  tds = strtok(NULL, ",");            //let's pars the string at each comma.
  sal = strtok(NULL, ",");            //let's pars the string at each comma.
  sg = strtok(NULL, ",");             //let's pars the string at each comma.

//  Serial.print("EC:");                //we now print each value we parsed separately.
//  Serial.println(ec);                 //this is the EC value.
  Spark.publish("EC", String(ec));
  
//  Serial.print("TDS:");               //we now print each value we parsed separately.
//  Serial.println(tds);                //this is the TDS value.
  Spark.publish("TDS", String(tds));
  
//  Serial.print("SAL:");               //we now print each value we parsed separately.
//  Serial.println(sal);                //this is the salinity value.
  Spark.publish("SAL", String(sal));
  
//  Serial.print("SG:");               //we now print each value we parsed separately.
//  Serial.println(sg);                //this is the specific gravity.
  Spark.publish("SG", String(sg));
  
  //uncomment this section if you want to take the values and convert them into floating point number.
  /*
  ec_float=atof(ec);
  tds_float=atof(tds);
  sal_float=atof(sal);
  sg_float=atof(sg);
  */
}
3 Likes

Hey Gavin,

Thanks for the help! I will give this a go as soon as I can. Ill keep you posted with how it goes.

Cheers,

Mason

Hey Gavin!

Fantastic it worked a charm! I will start playing with your code. What are you working on?

It would be great to hear what people are developing.

Cheers,

Mason

Have you ever used https://www.whiteboxes.ch/tentacle/docs/ ?

1 Like

I’ve seen the tentacle shield, but I’ve never used it. To me it seems like a convenience thing and not a necessity. If you’re comfortable breadboarding the atlas shields then you shouldn’t need the tentacle shield.

One thing that you might want to watch out for if you start using more than one Atlas sensor at a time is signal/power noise/interference. I noticed that I would get different readings if I only had one sensor in my water tank at a time, but when I added another sensor my readings were slightly off(pumps and other items can affect readings as well. I added an atlas Power-ISO board to feed my PH and EC sensors through and the issue went away, so you might want to play around with whatever you’ve got going on before trusting that the sensors are reading accurately by trying them out one at a time and then comparing those readings to when everything’s added to see if there’s a difference.

One more issue I’ve run into is that two times after I powered everything off and turned it back on the EZO boards had an unusual blink pattern and wouldn’t take readings. I had to follow the instructions for manually putting the boards back in i2C mode. If you open up the PDF manual for the EZO board and go to the page called “Manual switching to I2C mode” it’ll walk you through that procedure(it’s easy enough but left me scratching my head for a while until I tried that).

The project I’m using it for involves 3d printing seed pods for a hydroponics setup and then I’m hoping to automate my PH and EC levels which is where the Atlas sensors will come into play. My wiring’s a confusing mess of tangles right now, but here’s a picture of the seed pod.

2 Likes

@Gavin, that’s awesome work!

I’m currently playing with an Electron, but attempting to use the UART instead of I2C for communicating with the EC stamp.

Did you have a chance to try using UART instead of I2C? I’m struggling to get my pH stamp to return any values to my serial monitor.

I’ve included my code below, which is pretty much the same as the Arduino Uno sample code from their website.

#include "application.h"

String sensorstring = "";                             //a string to hold the data from the Atlas Scientific product
boolean sensor_string_complete = false;               //have we received all the data from the Atlas Scientific product
float pH;                                             //used to hold a floating point number that is the pH

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

void loop() {
  
   Serial1.print('r');
   Serial1.print("\r");

  if (Serial1.available() > 0) {                     //if we see that the Atlas Scientific product has sent a character
    char inchar = (char)Serial1.read();              //get the char we just received
    sensorstring += inchar;                           //add the char to the var called sensorstring
    if (inchar == '\r') {                             //if the incoming character is a <CR>
      sensor_string_complete = true;                  //set the flag
    }
  }


  if (sensor_string_complete == true) {               //if a string from the Atlas Scientific product has been received in its entirety
    Serial.println(sensorstring);                     //send that string to the PC's serial monitor
    if (isdigit(sensorstring[0])) {                   //if the first character in the string is a digit
      pH = sensorstring.toFloat();                    //convert the string to a floating point number so it can be evaluated by the Arduino
      if (pH >= 7.0) {                                //if the pH is greater than or equal to 7.0
        Serial.println("high");                       //print "high" this is demonstrating that the Arduino is evaluating the pH as a number and not as a string
      }
      if (pH <= 6.999) {                              //if the pH is less than or equal to 6.999
        Serial.println("low");                        //print "low" this is demonstrating that the Arduino is evaluating the pH as a number and not as a string
      }
    }
    sensorstring = "";                                //clear the string
    sensor_string_complete = false;                   //reset the flag used to tell if we have received a completed string from the Atlas Scientific product
  }

  delay(1000);
}

Thanks in advance!

I think part of the priblem might be that you send "r\r" each time round loop() but only ever read one byte back from the sensor.

Try to do that only after you received your expected response or after a set timeout.
Also try just feeding back your received characters as soon you read them to see what you actually get.
Additionally change your if(Serial1.available()) for a while(Serial1.available()) in order to grab your response as quick as possible.
Adding a short delay(100) after Serial.print("r\r") might even be enough to catch the whole lot in one go.

@ScruffR - you’re a gentleman and a scholar.

I got it working, and it also resolved my earlier hard fault issues (SOS & 1 red blink).

The following is the code I’ve used successfully. Any ideas for improvements are most welcome!

#include "application.h"

String sensorstring = "";                             //a string to hold the data from the Atlas Scientific product
boolean sensor_string_complete = false;               //have we received all the data from the Atlas Scientific product
float pH;                                             //used to hold a floating point number that is the pH
int oldTime = 0;

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

void loop() {
  Serial.println('loop begin');
   Serial1.print('r');
   Serial1.print('\r');
   delay(100);

    oldTime = millis();
    while(sensor_string_complete == false && millis()-oldTime < 5000) {
        while(Serial1.available()) {                                        //if we see that the Atlas Scientific product has sent a character
            char inchar = (char)Serial1.read();                             //get the char we just received
            sensorstring += inchar;                                         //add the char to the var called sensorstring
            if (inchar == '\r') {                                           //if the incoming character is a <CR>
                sensor_string_complete = true;                              //set the flag
            }
        }
    }

  if (sensor_string_complete == true) {               //if a string from the Atlas Scientific product has been received in its entirety
    Serial.println(sensorstring); 
    if (isdigit(sensorstring[0])) {                   //if the first character in the string is a digit
      pH = sensorstring.toFloat();                    //convert the string to a floating point number so it can be evaluated by the Arduino
      if (pH >= 7.0) {                                //if the pH is greater than or equal to 7.0
        Serial.println("high");                       //print "high" this is demonstrating that the Arduino is evaluating the pH as a number and not as a string
      }
      if (pH <= 6.999) {                              //if the pH is less than or equal to 6.999
        Serial.println("low");                        //print "low" this is demonstrating that the Arduino is evaluating the pH as a number and not as a string
      }
    }
    sensorstring = "";                                //clear the string
    sensor_string_complete = false;                   //reset the flag used to tell if we have received a completed string from the Atlas Scientific product
  }
  
  delay(1000);
}

Additionally, I’ve tested that the code works on both Photons and Electrons.

1 Like

Do you guys also the i2c dropping out into some default mode randomly and it wont respond to any commands?

And the only way to fix it is a manual change to i2c.

How have you guys solved this?

Welcome :+1:

Actually I meant to wrap the Serial.print() statements in an if() block, but this might be even more compact

void loop()
{
  Serial1.flush();                               // this is optional
  Serial1.print("r\r");
  sensorstring = Serial1.readStringUntil('\r');  // instead of the loop
  Serial.println(sensorstring); 
  if(pH = sensorstring.toFloat()) // pH would be 0.0 if the conversion failed and 0.0 is not likely a valid value anyway  
    Serial.println(pH >= 7.0 ? "high" : "low");
  delay(1000); 
}

See here for readStringUntil()

1 Like

I’ve had this happen as well and had to manually force it back to i2c mode. Once I was done messing with my code and flashing the photon the problem seemed to go away. It’s been running for well over a month with no issues.

I contacted Atlas scientific, it is a hardware issue. If it shuts off at 900ms it will go into default mode and there is no way to send a message over the air to fix it. Though the latest new ezo chip has solved this.

Hey does anyone have an example of two sensors or more?

For example a pH and EC probe both sending data?

I want to see how people are coding theirs?

Cheers,

Erkele

Ive made a generic ezo command function cribed mainly from the published code.

String ezoCommand(String command, int address){

  byte code=0;                     //used to hold the I2C response code. 
  char ezo_data[48];                //we make a 20 byte character array to hold incoming data from the pH circuit. 
  byte in_char=0;                  //used as a 1 byte buffer to store in bound bytes from the pH Circuit.   
  byte i=0;                        //counter used for ph_data array. 
  int time_=1800; 

   if(command =="Cal,mid,7.00" || command =="Cal,high,10.00" || command =="Cal,low,4.00" || command=="r")time_=1800; //if a command has been sent to calibrate or take a reading we wait 1800ms so that the circuit has time to take the reading.  
       else time_=300; 

    syslog(5,command);
    syslog(5,String(time_));

    Wire.beginTransmission(address); //call the circuit by its ID number.  
    Wire.write(command);        //transmit the command that was sent through the serial port.
    Wire.endTransmission();          //end the I2C data transmission. 

    delay(time_); 

    Wire.requestFrom(address,48,1); //call the circuit and request 20 bytes (this may be more than we need)
    code=Wire.read(); 

    while(Wire.available()){          //are there bytes to receive.  
     in_char = Wire.read();           //receive a byte.
     ezo_data[i]= in_char;             //load this byte into our array.
     i+=1;                            //incur the counter for the array element. 
      if(in_char==0){                 //if we see that we have been sent a null command. 
          i=0;                        //reset the counter i to 0.
          Wire.endTransmission();     //end the I2C data transmission.
          break;                      //exit the while loop.
      }
    }

    syslog(5,String(code));   

    switch (code){                  //switch case based on what the response code is.  
      case 1:                       //decimal 1.  
        syslog(5,"Success");  //means the command was successful.
      break;                        //exits the switch case.

     case 2:                        //decimal 2. 
       syslog(5,"Failed");    //means the command has failed.
     break;                         //exits the switch case.

     case 254:                      //decimal 254.
       syslog(5,"Pending");   //means the command has not yet been finished calculating.
     break;                         //exits the switch case.

     case 255:                      //decimal 255.
       syslog(5,"No Data");   //means there is no further data to send.
     break;                         //exits the switch case.
    }

    syslog(5,ezo_data);         //print the data.  

    if (code == 1){
    return ezo_data ;
    } 
    else {
      return 0;
    }      
}

Then I call that function and pass the i2c address to talk to the different sensors.

int phRead(String command){
  ezoCommand("r",99);
  return 1;
}

int ecRead(String command){
  ezoCommand("r",100);
  return 1;
}

I’m logging via a syslog function im working on rather than via serial but you could easily adapt the code i’ve posted.

It’s all a work in process at the moment.

Hey,

That is very different, I might have a shot at it and look at what happens. It is very short and compact coding, i like it!

What project are you running? I am running a code similar to Gavin now and it works really well. Though I have had a few red light situations.

Cheers,

Erkele

 I added an atlas Power-ISO board to feed my PH and EC sensors through and the issue went away,

I am working on a similar hydroponics project. Do you need one POWER-ISO board per sensor? Can you share a screenshot of how you wired up both the modules on the breadboard?

Thanks for helping out.

Hey Ali,

Here is a link to the wiring guide:
http://www.atlas-scientific.com/_files/_datasheets/_circuit/pwr-iso.pdf

Though I use this for 4 probes, I run ph, ORP, DO, and EC. It is cheaper to use this I think if you have a lot of sensors. You dont need to use the code that comes with it.
http://www.atlas-scientific.com/product_pages/components/tentacle-shield.html

And I just modified the code to read from each sensor one after another.