I'm using the code below to try out my pH microcontroller from Atlas Scientific. I'm a newbie but I think all the connections are right (the only think different is that I'm using a 3.3V Arduino Pro Mini instead of a 5V board).
I seem to get a temperature reading and a pH value but as you can see in the photo, the pH characters become garbled. Is there any chance anyone might know what I could be doing wrong? I've included a pic of the circuit as well if that helps.
/*********************************************************
H2O pH Probe
Joel Bartlett
SparkFun Electronics
October 25, 2013
License:
This code is beerware: feel free to use it, with or without attribution,
in your own projects. If you find it helpful, buy me a beer next time you
see me at the local pub.
This code is for the H20 pH Probe - a handheld device that uses an Arduino
Pro Mini, a Serial Enabled LCD, a DS18B20 waterproof temerature sensor,
and the pH kit from Atlas Scientific to quickly and accurately measure the pH
of any liquid.
This code was written with Arduino 1.0.5 (available at arduino.cc) It should
work with any Arduino IDE version from 1.0 and on.
This uses some code found in the example skecth provided by Atlas Scientific for the pH stamp,
the DS18B20 example sketch found on bildr - http://bildr.org/2011/07/ds18b20-arduino/
and some of the functions for the SerLCD Library http://playground.arduino.cc/Learning/SparkFunSerLCD
***********************************************************/
#include <SoftwareSerial.h> // include the software serial library to add an aditional serial port to talk to the pH stamp
#include <OneWire.h>// include the oneWire library to communcate with the temerature sensor
//Create an instance of the softwareSerial class named pHserial
SoftwareSerial pHserial(2,3); // (RX,TX)
OneWire ds(12); //create an instance of the oneWire class named ds, communication will happen on pin 12
char inputBuffer[10]; // For incoming serial data
int button1 = 6; //calibrate button
int button2 = 7; //test button
int tmpPwr = 10; //power line for the temp sensor
//-----------------------------------------------------------
void setup()
{
pinMode(button1, INPUT); //make button1 an input
digitalWrite(button1, HIGH); //turn on internal pull-up resistor
pinMode(button2, INPUT); //make button2 an input
digitalWrite(button2, HIGH); //turn on internal pull-up resistor
pHserial.begin(38400);//the pH stamp communicates at 38400 baud by default
Serial.begin(9600);//the LCD commincates at 9600 baud by default
getTemp();//the first value seems to be a throwaway value, so we call it once here to get rid of the garbage value.
pinMode(tmpPwr, OUTPUT);
digitalWrite(tmpPwr, HIGH);//it was easier to attach this temp sensor's power pin near to the signal
//pin on account of the 4.7Kohm resistor needed between them.
//backlightHalf();
//this function was only called once to set the backlight at half brightness. When it was at full brightness,
//the whole circuit pulled a little more curent than the 9V battery could provide.
}
//-----------------------------------------------------------
void loop()
{
if(digitalRead(button1) == LOW) //if button1 is pressed, calibrate
{
clearLCD();// clear the LCD screen
selectLineOne();//move up to the first line of the LCD
goTo(3);//move in to position 3 on the LCD for centered text
Serial.print("Calibrating");
calibrate();
delay(500);//debounce
}
if(digitalRead(button2) == LOW) //if button2 is pressed, read pH and temp once
{
clearLCD();// clear the LCD screen
selectLineOne();//move up to the first line of the LCD
goTo(3);//move in to position 3 on the LCD
Serial.print("Temp=");//print the current temp in degrees F
Serial.print(convertTemp());
Serial.print("F");
selectLineTwo();//move down to the second line of the LCD
goTo(21);//move in to position 21
Serial.print("pH=");//print the current pH
getPh();
delay(500);//debounce
}
}
//-----------------------------------------------------------
float getTemp()
{
//returns the temperature from one DS18S20 in degrees C
byte data[12];
byte addr[8];
if ( !ds.search(addr)) {
//no more sensors on chain, reset search
ds.reset_search();
return -1000;
}
if ( OneWire::crc8( addr, 7) != addr[7]) {
//Serial.println("CRC is not valid!");
return -1000;
}
if ( addr[0] != 0x10 && addr[0] != 0x28) {
//Serial.print("Device is not recognized");
return -1000;
}
ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end
byte present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for (int i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
ds.reset_search();
byte MSB = data[1];
byte LSB = data[0];
float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;
return TemperatureSum; // returns the temeperature in degrees C
}
//-----------------------------------------------------------
float convertTemp()
{
//This function converts the temerature from celcius to farenheit
float temperature = getTemp();
float tempF = temperature * 1.8 + 32;
return(tempF);
}
//-----------------------------------------------------------
void getPh()
{
//This function queries the pH stamp to return one reading of pH
pHserial.print("R\r"); //send R followed by a carriage return prompts the stamp to send back a single pH reading
delay(10);
pHserial.readBytesUntil(13,inputBuffer,20);//this reads back the results into the inputBuffer we created until it sees
//a carriage return (13) or until it reaches 20 bytes (which it shouldn't)
delay(500);
Serial.print(inputBuffer);// print the pH value to the Serial port, which is connected to the LCD.
}
//-----------------------------------------------------------
void calibrate()
{
//This function calibrates the pH stamp with the current temperature read from the DS18B20 temp sensor
delay(500);
pHserial.print(getTemp()); //pritn temp in degrees C to Ph Sensor
pHserial.write("\r"); //carrage return
delay(1000);
clearLCD();
selectLineOne();//move up to the first line of the LCD
goTo(4);//Move in 4 spaces on the second line to center text
Serial.print("Complete");
selectLineTwo();//move down to the second line of the LCD
goTo(19);//move in to position 19 to center text
Serial.print("Temp=");//print the temperture that was used to calibrate to the LCD
Serial.print(convertTemp());
Serial.print("F");
delay(500);
}
//-----------------------------------------------------------
// Resets the display, undoing any scroll and removing all text
void clearLCD()
{
Serial.write(0xFE);
Serial.write(0x01);
}
//-----------------------------------------------------------
// Starts the cursor at the beginning of the first line (convienence method for goTo(0))
void selectLineOne()
{ //puts the cursor at line 0 char 0.
Serial.write(0xFE); //command flag
Serial.write(128); //position
}
//-----------------------------------------------------------
// Starts the cursor at the beginning of the second line (convienence method for goTo(16))
void selectLineTwo()
{ //puts the cursor at line 1 char 0.
Serial.write(0xFE); //command flag
Serial.write(192); //position
}
//-----------------------------------------------------------
// Sets the cursor to the given position
// line 1: 0-15, line 2: 16-31, 31+ defaults back to 0
void goTo(int position)
{
if (position < 16)
{
Serial.write(0xFE); //command flag
Serial.write((position+128));
} else if (position < 32)
{
Serial.write(0xFE); //command flag
Serial.write((position+48+128));
} else
{
goTo(0);
}
}
//-----------------------------------------------------------
// Turns the backlight on, full brightness
void backlightOn()
{
Serial.write(0xFE);
Serial.write(157);
}
//-----------------------------------------------------------
// Turns the backlight to half brightness
void backlightHalf()
{
Serial.write(0xFE);
Serial.write(140);
}
//-----------------------------------------------------------
You just aren't formatting the text properly. Numbers vary in lengtth and sign. The simplest solution is to add a couple of spaces after the output to ensure you are covering over what has been printed before.
If you trimmed down the pictures to show just what you need to show, you will get a lot more friends. Try Irfanview for free, and use the CTRL-Y trimmer
Thank you for taking the time to reply, I've edited the photos. Forgive my ignorance, but if it's not too much trouble could you show me what you mean so I know how to change the code? I'm having a bit of trouble figuring out how to do it.
Is it this part of the code I need to change:
void calibrate()
{
//This function calibrates the pH stamp with the current temperature read from the DS18B20 temp sensor
delay(500);
pHserial.print(getTemp()); //pritn temp in degrees C to Ph Sensor
pHserial.write("\r"); //carrage return
delay(1000);
clearLCD();
selectLineOne();//move up to the first line of the LCD
goTo(4);//Move in 4 spaces on the second line to center text
Serial.print("Complete");
selectLineTwo();//move down to the second line of the LCD
goTo(19);//move in to position 19 to center text
Serial.print("Temp=");//print the temperture that was used to calibrate to the LCD
Serial.print(convertTemp());
Serial.print("F");
delay(500);
Say the first number is 12.52 and it prints kosher
Next number is 9.63 but the same cursor position would only overprint 12.5, and the result would appear to be 9.632. BUT if it was -9.63 it would be fine as it has the same number of characters. You can fix the first problem by printing the number and follow immediately by two blanks " ". Having two would fix 9.63 following -12.52. There are other ways of doing this that are smarter. If you assess the number before printing, you can lessen the risk of putting a blank over something you actually want to keep and also keep the decimal points lined up.
So if I wanted to increase the character length to accept an extra couple of characters do you know which part of the code would I have to change? I just can't work out which part it actually is. In any case I'm a bit confused because the pH, being in the buffer solution stays at 3.99 but I get all sorts of strange characters even at the beginning character. Anyway hopefully the formatting will work as you say when I work out how to do it.
No. The calibration code is not printing the incorrect data.
getPh();
This is crap. A function with get in it's name should return a value. It should NOT print the value to the serial port.
pHserial.readBytesUntil(13,inputBuffer,20);//this reads back the results into the inputBuffer we created until it sees
This function returns the number of bytes it wrote to the buffer. Why are you discarding that value? You need it to make a string (a NULL terminated array of chars) from inputBuffer (an array of chars that is NOT null terminated).
Serial.print(inputBuffer);// print the pH value to the Serial port, which is connected to the LCD.
This function expects a string. inputBuffer is NOT a string.
You need to get the number of characters that readBytesUntil actually read, and then you need to stuff a NULL into the array at that position.
int cc = pHserial.readBytesUntil('\n', inputBuffer, 20); // Notice the spaces to make this more readable
if(cc < 20)
inputBuffer[cc] = '\0'; // Add the NULL
Serial.print(inputBuffer); // Viola, no more garage
Serial.print(" "); // blank any characters printed last time that were not overwritten
olly_whinnett:
do you know which part of the code would I have to change?
I'm afraid I can't comment because I don't understand the code. I just collect numbers from sensors and send them off to various destinations. You seem to be doing more than that.
Anyway hopefully the formatting will work as you say when I work out how to do it.
I think you are right, you just need to attend to that. You might Serial.println to the monitor at the same time in order to see what you should see.
PaulS I really appreciate your help, although I still seem to be having some difficulty. I changed the code to what's shown below as you suggested, but as the photos shows, the pH doesn't show. I tried the pH stamp on the serial monitor using the code supplied by Atlas scientific and I get a value so I don't understand why it isn't working. I've probably missed something obvious but I can't figure out what yet.
/*********************************************************
H2O pH Probe
Joel Bartlett
SparkFun Electronics
October 25, 2013
License:
This code is beerware: feel free to use it, with or without attribution,
in your own projects. If you find it helpful, buy me a beer next time you
see me at the local pub.
This code is for the H20 pH Probe - a handheld device that uses an Arduino
Pro Mini, a Serial Enabled LCD, a DS18B20 waterproof temerature sensor,
and the pH kit from Atlas Scientific to quickly and accurately measure the pH
of any liquid.
This code was written with Arduino 1.0.5 (available at arduino.cc) It should
work with any Arduino IDE version from 1.0 and on.
This uses some code found in the example skecth provided by Atlas Scientific for the pH stamp,
the DS18B20 example sketch found on bildr - http://bildr.org/2011/07/ds18b20-arduino/
and some of the functions for the SerLCD Library http://playground.arduino.cc/Learning/SparkFunSerLCD
***********************************************************/
#include <SoftwareSerial.h> // include the software serial library to add an aditional serial port to talk to the pH stamp
#include <OneWire.h>// include the oneWire library to communcate with the temerature sensor
//Create an instance of the softwareSerial class named pHserial
SoftwareSerial pHserial(2,3); // (RX,TX)
OneWire ds(12); //create an instance of the oneWire class named ds, communication will happen on pin 12
char inputBuffer[10]; // For incoming serial data
int button1 = 6; //calibrate button
int button2 = 7; //test button
int tmpPwr = 10; //power line for the temp sensor
//-----------------------------------------------------------
void setup()
{
pinMode(button1, INPUT); //make button1 an input
digitalWrite(button1, HIGH); //turn on internal pull-up resistor
pinMode(button2, INPUT); //make button2 an input
digitalWrite(button2, HIGH); //turn on internal pull-up resistor
pHserial.begin(38400);//the pH stamp communicates at 38400 baud by default
Serial.begin(9600);//the LCD commincates at 9600 baud by default
getTemp();//the first value seems to be a throwaway value, so we call it once here to get rid of the garbage value.
pinMode(tmpPwr, OUTPUT);
digitalWrite(tmpPwr, HIGH);//it was easier to attach this temp sensor's power pin near to the signal
//pin on account of the 4.7Kohm resistor needed between them.
//backlightHalf();
//this function was only called once to set the backlight at half brightness. When it was at full brightness,
//the whole circuit pulled a little more curent than the 9V battery could provide.
}
//-----------------------------------------------------------
void loop()
{
if(digitalRead(button1) == LOW) //if button1 is pressed, calibrate
{
clearLCD();// clear the LCD screen
selectLineOne();//move up to the first line of the LCD
goTo(3);//move in to position 3 on the LCD for centered text
Serial.print("Calibrating");
calibrate();
delay(500);//debounce
}
if(digitalRead(button2) == LOW) //if button2 is pressed, read pH and temp once
{
clearLCD();// clear the LCD screen
selectLineOne();//move up to the first line of the LCD
goTo(3);//move in to position 3 on the LCD
Serial.print("Temp=");//print the current temp in degrees F
Serial.print(convertTemp());
Serial.print("F");
selectLineTwo();//move down to the second line of the LCD
goTo(21);//move in to position 21
Serial.print("pH=");//print the current pH
getPh();
delay(500);//debounce
}
}
//-----------------------------------------------------------
float getTemp()
{
//returns the temperature from one DS18S20 in degrees C
byte data[12];
byte addr[8];
if ( !ds.search(addr)) {
//no more sensors on chain, reset search
ds.reset_search();
return -1000;
}
if ( OneWire::crc8( addr, 7) != addr[7]) {
//Serial.println("CRC is not valid!");
return -1000;
}
if ( addr[0] != 0x10 && addr[0] != 0x28) {
//Serial.print("Device is not recognized");
return -1000;
}
ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end
byte present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for (int i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
ds.reset_search();
byte MSB = data[1];
byte LSB = data[0];
float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;
return TemperatureSum; // returns the temeperature in degrees C
}
//-----------------------------------------------------------
float convertTemp()
{
//This function converts the temerature from celcius to farenheit
float temperature = getTemp();
float tempF = temperature * 1.8 + 32;
return(tempF);
}
//-----------------------------------------------------------
void getPh()
{
int cc = pHserial.readBytesUntil('\n', inputBuffer, 20); // Notice the spaces to make this more readable
if(cc < 20)
inputBuffer[cc] = '\0'; // Add the NULL
Serial.print(inputBuffer); // Viola, no more garage
Serial.print(" "); // blank any characters printed last time that were not overwritten
}
//-----------------------------------------------------------
void calibrate()
{
//This function calibrates the pH stamp with the current temperature read from the DS18B20 temp sensor
delay(500);
pHserial.print(getTemp()); //pritn temp in degrees C to Ph Sensor
pHserial.write("\r"); //carrage return
delay(1000);
clearLCD();
selectLineOne();//move up to the first line of the LCD
goTo(4);//Move in 4 spaces on the second line to center text
Serial.print("Complete");
selectLineTwo();//move down to the second line of the LCD
goTo(19);//move in to position 19 to center text
Serial.print("Temp=");//print the temperture that was used to calibrate to the LCD
Serial.print(convertTemp());
Serial.print("F");
delay(500);
}
//-----------------------------------------------------------
// Resets the display, undoing any scroll and removing all text
void clearLCD()
{
Serial.write(0xFE);
Serial.write(0x01);
}
//-----------------------------------------------------------
// Starts the cursor at the beginning of the first line (convienence method for goTo(0))
void selectLineOne()
{ //puts the cursor at line 0 char 0.
Serial.write(0xFE); //command flag
Serial.write(128); //position
}
//-----------------------------------------------------------
// Starts the cursor at the beginning of the second line (convienence method for goTo(16))
void selectLineTwo()
{ //puts the cursor at line 1 char 0.
Serial.write(0xFE); //command flag
Serial.write(192); //position
}
//-----------------------------------------------------------
// Sets the cursor to the given position
// line 1: 0-15, line 2: 16-31, 31+ defaults back to 0
void goTo(int position)
{
if (position < 16)
{
Serial.write(0xFE); //command flag
Serial.write((position+128));
} else if (position < 32)
{
Serial.write(0xFE); //command flag
Serial.write((position+48+128));
} else
{
goTo(0);
}
}
//-----------------------------------------------------------
// Turns the backlight on, full brightness
void backlightOn()
{
Serial.write(0xFE);
Serial.write(157);
}
//-----------------------------------------------------------
// Turns the backlight to half brightness
void backlightHalf()
{
Serial.write(0xFE);
Serial.write(140);
}
//-----------------------------------------------------------
[/quote]
As I said, I can't comment because I don't understand your code. I have just come to understand why I don't. You are using a serial LCD and my character LCDs are plain vanilla parallel or I2C. I might add that your code is the best reason I have ever seen for not getting a serial LCD, but that doesn't alter the fact that there is nothing fundamentally wrong with it, and the last line of your previous is probably correct - something stupid and that will only happen once.
You might find that there are libraries around for serial LCDs that will go a long way to reducing your grief.
I suggest you re-post this, or alter the title in order to clearly indicate this is a serial LCD problem. It is not a problem exclusive to serial LCD but that might attract somebody more qualified than me.
Apologies Nick, the post above was actually meant as a reply to PaulS. I should have quoted his post but I was running out of character space so I just didn't bother in the end.
I do actually have an I2C LCD but I bought the serial LCD specifically because it makes it easier to use with this code (which I myself didn't write by the way, which I should have said).
I'll wait until PaulS gets a chance to reply but if I don't have any luck then I will consider doing exactly what you suggest - reposting it with a better title. I am grateful for your useful input though.
OK, I guess the main priority is brewing beer, not writing code. You might laugh about this in the future but I think you would ultimately have been better off adapting the code to use the LCD you already had - not that that would have solved this immediate problem!
^ absolutely, that's the priority (well growing plants, rather, but a similar application). The plan was to sort of learn a bit as I go along, too though but in the meantime getting it working is the most important thing.
Anyone? I hate to bump my own post but I'm desperate. I have been reading and trying to get to grips with what the problem is but I'm not making any headway. Any help would be such appreciated.