Atlas Scientific pH Circuit

Hallo

Ich möchte gerne das PH Shield von Atlas http://www.atlas-scientific.com/product_pages/embedded/ph.html für die PH Steuerung meines Aquarium benutzen. Nur komme ich mir den EZ_com_mega sketch nicht klar im Serial Monitor funktioniert auch alles wunder bar aber ich bekomme es nicht ihn das es auf ein LCD-Display läuft.

Kann mir da vielleicht einer weiter helfen?

Was siehst Du auf dem Display? Bitte den Sketch und Modell des Displays. (oder alternativ eine Anleitung zum Gedankenlesen :wink: :wink: :wink: )
Grüße Uwe

Morgen alles zusammen

Also es handelt sich um ein Mega und ein 20X4 LCD-Display I2C
Ihr ist der EZ_com_mega sketch

/*
This software was made to demonstrate how to quickly get your Atlas Scientific product running on the Arduino platform.
An Arduino MEGA 2560 board was used to test this code.
This code was written in the Arudino 1.0 IDE
Modify the code to fit your system.
**Type in a command in the serial monitor and the Atlas Scientific product will respond.**
**The data from the Atlas Scientific product will come out on the serial monitor.**
Code efficacy was NOT considered, this is a demo only.
The TX3 line goes to the RX pin of your product.
The RX3 line goes to the TX pin of your product.
Make sure you also connect to power and GND pins to power and a common ground.
Open TOOLS > serial monitor, set the serial monitor to the correct serial port and set the baud rate to 38400.
Remember, select carriage return from the drop down menu next to the baud rate selection; not "both NL & CR".
*/



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_0 to 38400
     Serial3.begin(38400);                                                     //set baud rate for software serial port_3 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 Atlas Scientific product
     }
 
 
 
   void serialEvent() {                                                         //if the hardware serial port_0 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 serialEvent3(){                                                         //if the hardware serial port_3 receives a char 
              char inchar = (char)Serial3.read();                              //get the char we just received
              sensorstring += inchar;                                          //add it to the inputString
              if(inchar == '\r') {sensor_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 entierty 
      Serial3.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 completed string from the PC
      }

 if (sensor_stringcomplete){                                                   //if a string from the Atlas Scientific product has been recived in its entierty 
      Serial.println(sensorstring);                                            //send that string to to the PC's serial monitor
      sensorstring = "";                                                       //clear the string:
      sensor_stringcomplete = false;                                           //reset the flage used to tell if we have recived a completed string from the Atlas Scientific product
      }
 }

und ihr der wo ich ein LCD-Display ein gefügt habe

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

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

//LCD_Display 
LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7);    //0x3F Test   //0x27

  void setup(){                                                                //set up the hardware
     Serial.begin(38400);                                                      //set baud rate for the hardware serial port_0 to 38400
     Serial3.begin(38400);                                                     //set baud rate for software serial port_3 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 Atlas Scientific product 
     
lcd.begin (20,4); 
lcd.setBacklightPin(3,POSITIVE); 
lcd.setBacklight(HIGH); 
lcd.clear();
     }
 
 
 
   void serialEvent() {                                                         //if the hardware serial port_0 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 serialEvent3(){                                                         //if the hardware serial port_3 receives a char 
              char inchar = (char)Serial3.read();                              //get the char we just received
              sensorstring += inchar;                                          //add it to the inputString
              if(inchar == '\r') {sensor_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 entierty 
      Serial3.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 completed string from the PC
      }

 if (sensor_stringcomplete){                                                   //if a string from the Atlas Scientific product has been recived in its entierty 
      Serial.println(sensorstring);                                            //send that string to to the PC's serial monitor
      sensorstring = "";                                                       //clear the string:
      sensor_stringcomplete = false;                                           //reset the flage used to tell if we have recived a completed string from the Atlas Scientific product
      }


    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("PH ");
    lcd.print(sensorstring);
    delay(500);
 }

Nun erscheint auf dem Display immer so der PH Wert
PH 7
pause
PH 7.
pause
PH 7.5
pause
PH 7.59
pause
PH
pause
PH 7
pause
PH 7.5
pause
PH 7.59
pause
PH
pause
PH 7
pause
und immer so weiter.

Und wenn ichfloat PH = sensorstring;
einsetzte dann erscheint Vollgene Fehler Meldung: cannot convert 'String' to 'float' in initialization

Ingo79:
Und wenn ichfloat PH = sensorstring;
einsetzte dann erscheint Vollgene Fehler Meldung: cannot convert 'String' to 'float' in initialization

Ohauahauaha.

Das erste, was Du dann machen könntest, wenn Du ein vorhandenes Programm modifizieren möchtest, wäre das Lesen der Kommentare.

Und daraus sollte sich mit einem Mindestmaß an Leseverstehen doch ergeben, dass Du die Ausgabe des Werts in den if-Block setzen mußt, der immer ausgeführt wird, wenn sensor_stringcomplete ist.

Also wenn der komplette String vom Sensor empfangen wurde, dann einen Codeblock ausführen mit:

  • Ausgabe sensorstring auf Serial
  • lcd löschen
  • lcd Cursorposition setzen (ist nach dem Löschen wohl überflüssig)
  • Ausgabe "PH " auf lcd
  • Ausgabe sensorstring auf lcd
  • sensorstring löschen

Die LCD Ausgabe könnte man noch optimieren, indem man das Display zwischendurch nicht immer löscht und den Inhalt komplett neu ausgibt, sondern nur den Zahlenwert aktualisiert, aber für den Anfang...

 if (sensor_stringcomplete)
 {                                   //if a string from the Atlas Scientific product has been recived in its entierty 
    Serial.println(sensorstring);    //send that string to to the PC's serial monitor
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("PH ");
    lcd.print(sensorstring);
    sensorstring = "";               //clear the string:
    sensor_stringcomplete = false;   //reset the flage used to tell if we have recived a completed string from the Atlas Scientific product
  }

Und wenn ich
Code:

float PH = sensorstring;

einsetzte dann erscheint Vollgene Fehler Meldung: cannot convert 'String' to 'float' in initialization

Ich weigere mich mit Strings zu arbeiten.
Die Umwandlung von Array (mit einer null an letzter Stelle) zu Zahlen funktioniert mit
PH = atof(array).
Weiß nicht wie das mit Strings funktioniert.

Grüße Uwe

Hallo

Jetzt habe ich die Ausgabe des Werts in den if-Block funktioniert auch so weit bis auf das an der 3 stelle Hintern kommer so streifen in der Zelle sind.

Und wie kann ich den sensorstring umwandeln das ich den in float PH kriege um den PH wert auf SD Karte zu speichern beziehungsweise weiter zu verarbeiten um da mit ein Relais anzusteuern um mittels CO2 den PH Wert im Aquarium zu senken?

Ingo79:
Jetzt habe ich die Ausgabe des Werts in den if-Block funktioniert auch so weit bis auf das an der 3 stelle Hintern kommer so streifen in der Zelle sind.

Unsichtbare Steuerzeichen nicht mit in den sensorstring reinpacken, sondern rausfiltern.
Die "unsichtbaren" Steuerzeichen sind im Gegensatz zum seriellen Monitor nämlich auf dem LCD gar nicht so unsichtbar.

  void serialEvent3(){   //if the hardware serial port_3 receives a char 
              char inchar = (char)Serial3.read();   //get the char we just received
              if (inchar>=' ') sensorstring += inchar;  // nur sichtbare Zeichen in den sensorstring packen!
              if(inchar == '\r') {sensor_stringcomplete = true;}  //if the incoming character is a <CR>, set the flag 
}

Kommt das merkwürdige Zeichen am Ende dann immer noch?

Ingo79:
Und wie kann ich den sensorstring umwandeln das ich den in float PH kriege um den PH wert auf SD Karte zu speichern beziehungsweise weiter zu verarbeiten um da mit ein Relais anzusteuern um mittels CO2 den PH Wert im Aquarium zu senken?

Der Programmierer Deines Demo-Programms hat zum Auslesen als sensorstring Variable leider den total verkackten Datentyp "String-Objekt" genommen, der praktisch für nichts sinnvolles brauchbar ist, außer als abschreckendes Beispiel. String-Objekte sind zu sämtlichen Library-Funktionen der AVR libc vollkommen inkompatibel. Was das Objekt nicht eingebaut hat, kannst Du nur über eine Umwandlung in einen normalen C-String realisieren. Und dann mit einer Library wie "atof" (Array-to-float) die Umwandlung in float machen, nachdem Du Dir einen Zeiger auf einen C-String geholt hast.

Völlig hirnrissig kompliziert das mit String-Objekten zu machen, aber ich glaube so geht es:

  float f=atof((char*)&sensorstring[0]);

Das Programmieren mit den verkackten String-Objekten vom Deklarationstyp "String" gewöhnst Du Dir am besten gar nicht erst an, wenn Du Mikrocontroller programmieren möchtest. Der Datentyp ist viel zu wenig leistungsfähig, verbraucht viel RAM-Speicher und wird langsam verarbeitet. Das bringt Dir nichts ein als Ärger beim Umwandeln in richtige C-Strings (char-Arrays), mit denen alle Library-Funktionen arbeiten, wenn Du irgendwas machen möchtest, das bei diesem Objekttyp nicht bereits vorprogrammiert ist. Ich glaube, "String" wurde von den Arduino-Machern nur erfunden, um Noobs zu ärgern: Sieht auf den ersten Blick super-einfach aus, aber läßt sich praktisch nichts sinnvolles mit machen.

Ich habe im Web was gefunden wo mit ich glaube weiter komme. Hier ist der Link Aquarinium/webserver.pde at master · sergiomokshin/Aquarinium · GitHub
Da habe ich mir folgenes raus copyiert

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//PH
int indexPH;
char clientPH[3];
String sensorstring = "";
boolean sensor_stringcomplete = false;

LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7);    //0x3F Test   //0x27

void setup()
{
  Serial.begin(38400); 
  Serial3.begin(38400);
  
  lcd.begin(20, 4);
  lcd.setBacklightPin(3,POSITIVE); 
  lcd.setBacklight(HIGH);
  lcd.clear(); 
  indexPH = 0;
}

void loop()
{
  LeituraPH();
}

void LeituraPH() {

     Serial3.print("C\r");
     serialEvent3();
     
 if (sensor_stringcomplete){
      Serial.println(clientPH);
      delay(50);
      
      lcd.setCursor(0, 0);
      lcd.print("PH");
      
      lcd.setCursor(3, 0);
      lcd.print(clientPH);
      
      lcd.setCursor(7, 0);
      lcd.print("  ");
      
      indexPH = 0;
      sensorstring = "";
      sensor_stringcomplete = false;
      }
      
}

void serialEvent3(){
   
      char c = (char)Serial3.read();
      
      if(c == '\r'){
        sensor_stringcomplete = true;
      }
      else
      {
        if(validateNumber(c)){
          clientPH[indexPH] = c;
          indexPH++;
        }
      }
 }
bool validateNumber(char caracter) {
  return (caracter == '0' || caracter == '1' || caracter == '2' || caracter == '3' || caracter == '4' || caracter == '5' || caracter == '6' || caracter == '7' || caracter == '8' || caracter == '9' || caracter == '.');
}

Das funktioniere auch erst mal so weit. Da drauf wert ich wohl aufbauen auch wenn ich noch nicht zu 100% den code nachvollziehen kann scheint mir das aber der richtige weg zu sein.

Wenn du jetzt noch das String Objekt rausschmeißt und durch ein char Array passender Länge ersetzt (und dann am Ende korrekt mit '\0' abschließt wenn das CR kommt!), passt es langsam. Genau kann man nämlich von Serial in einen C-String einlesen.

C-Strings:

Und chars sind eigentlich nichts als Integer. Man kann daher sowas machen:

if((character >= '0' && character <= '9') || character == '.')

Oder man nimmt die fertige Funktion isdigit():
http://www.nongnu.org/avr-libc/user-manual/group__ctype.html#ga49b21b266576c2490dab1e8f897c801a

Hallo jurs

So funktioniert es auch

Kommt das merkwürdige Zeichen am Ende dann immer noch?

Ja aber Jetzt nicht mehr.

Und das mit float f=atof((char*)&sensorstring[0]);
funktioniert auch!

Hallo Serenifly

Kannst du mir das mit den isdigit(): genauer erklären?

Das macht genau was auf der Seite steht. Prüft ob die der char eine Zahl ist und liefert 1/0, d.h. true/false zurück

bool validateNumber(char caracter)
{
    return (isDigit(character) || character == '.')
}

isdigit() macht intern letztlich auch nichts anderes als das was ich vorher per Hand geschrieben habe

Gerade gesehen, dass diese Funktionen sogar noch mal in wcharacter.h dupliziert sind:
https://github.com/arduino/Arduino/blob/master/hardware/arduino/cores/arduino/WCharacter.h
Kapselt lediglich ctype.h mit einem boolean als Rückgabe-Wert, CamelCase Schreibweise und vielleicht leicht lesbareren Namen. Irgendwie auch wieder totaler Unsinn. Aber wenigstens sind die inline deklariert

Hallo Serenifly

So habe heute wieder Zeit gefunden um mich von den String Objekt zu trennen.
Meinst du dass so:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//PH
int indexPH;
char PH_Wert[3];
char sensorstring[3];
boolean sensor_stringcomplete = false;

LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7);    //0x3F Test   //0x27

void setup()
{
  Serial.begin(38400); 
  Serial3.begin(38400);
  
  lcd.begin(20, 4);
  lcd.setBacklightPin(3,POSITIVE); 
  lcd.setBacklight(HIGH);
  lcd.clear(); 
  indexPH = 0;
}

void loop()
{
  LeituraPH();
}

void LeituraPH() 
{

     Serial3.print("C\r");
     serialEvent3();
     
 if (sensor_stringcomplete)
 {
      Serial.println(PH_Wert);
      delay(50);
      
      lcd.setCursor(0, 0);
      lcd.print("PH");

      lcd.setCursor(3, 0);
      lcd.print( PH_Wert);

      indexPH = 0;
      sensorstring[3] = '\0';
      sensor_stringcomplete = false;
      }
      
}

void serialEvent3()
{
  char PH = (char)Serial3.read();

  if(PH == '\r')
  {
    sensor_stringcomplete = true;
  }
  else
  {
    if(validateNumber(PH))
    {
      PH_Wert[indexPH] = PH;
      indexPH++;
    }
  }
}

bool validateNumber(char caracter)
{
  return ((caracter >= '0' && caracter <= '9') || caracter == '.');
}

Vorsicht. Du produzierst da einen Puffer-Überlauf. Ein Array der Größe 3 hat die Indizes 0-2.

Dann musst du PH_Wert Null-Terminieren wenn der String komplett da ist. Bei dir geht das jetzt, da das Array global ist. Dann ist der letzte Index automatisch 0. Oder der erste Index des nächsten Arrays in das die Variable überläuft. Darauf kann man sich aber nicht verlassen. Gerade wenn mal eine unterschiedliche Anzahl von Zeichen eingelesen wird:

  if(PH == '\r')
  {
    sensor_stringcomplete = true;
    PH_Wert[index] = '\0'
  }

Du machst da zwar was mit sensorString[] aber das wird nirgends beschrieben was ich so sehe. Der Teil gehört von der Programm-Logik her auch in diese Auslesefunktion und nicht in die Auswertung.

Ansonsten darfst du SerialEvent() nicht per Hand aufrufen. Das ist mir erst jetzt aufgefallen :s
Das ist ein spezieller Interrupt-Handler der automatisch aufgerufen wird wenn Daten ankommen. Kann man machen. Braucht man aber nicht. Einfach die Funktion anders benennen und es geht auch wenn man immer per Hand abfragt.

Du kannst auch die Einlesefunktionen einen boolean zurückgeben lassen. Dann spart man sich die globale Variable. Ich mache es so (ist vom Prinzip her so wie bei dir):

const int SERIAL_BUFFER_SIZE = 31;
char serialBuffer[SERIAL_BUFFER_SIZE];

void loop()
{
   if(checkSerial())
   {
        ....        //Daten verarbeiten
   }
}

boolean checkSerial()
{
  static byte index;

  if(Serial.available() > 0)
  {		
    char c = Serial.read();
		
    if(c != '\n' && index < SERIAL_BUFFER_SIZE - 1)
    {
        serialBuffer[index++] = c;
    }
    else
    {
	serialBuffer[index] = '\0';
	index = 0;
	return true;
    }
  }
  return false;
}

Hallo Serenifly

Jetzt ist der Bahnhof perfekt.
In den Barspiel von Atlas Scientific EZ_com_mega sketch ruft man doch auch den SerialEvent() doch auch per Hand auf über den Serial Monitor? Du meinst dieses

void loop()
{
  LeituraPH();
}

void LeituraPH() 
{

     Serial3.print("C\r");
     serialEvent3();

Und wenn ich die Array der Größe 10 gebe ist das dann besser?

Habe das jetzt so gebändert

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//PH
int indexPH;
char PH_Wert[10];
char sensorstring[10];
boolean sensor_stringcomplete = false;

LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7);    //0x3F Test   //0x27

void setup()
{
  Serial.begin(38400); 
  Serial3.begin(38400);
  
  lcd.begin(20, 4);
  lcd.setBacklightPin(3,POSITIVE); 
  lcd.setBacklight(HIGH);
  lcd.clear(); 
  indexPH = 0;
}

void loop()
{
  LeituraPH();
}

void LeituraPH() 
{

     Serial3.print("C\r");
     serialEvent3();
     
 if (sensor_stringcomplete)
 {
      Serial.println(PH_Wert);
      delay(50);
      
      lcd.setCursor(0, 0);
      lcd.print("PH");

      lcd.setCursor(3, 0);
      lcd.print( PH_Wert);

      indexPH = 0;
      //sensorstring[3] = '\0';
      sensor_stringcomplete = false;
      }
      
}

void serialEvent3()
{
  char PH = (char)Serial3.read();

  if(PH == '\r')
  {
    sensor_stringcomplete = true;
  }
  else
  {
    if(validateNumber(PH))
    {
      PH_Wert[indexPH] = PH;
      sensorstring[3] = '\0';
      indexPH++;
    }
  }
}

bool validateNumber(char caracter)
{
  return ((caracter >= '0' && caracter <= '9') || caracter == '.');
}

Das geht vielleicht, aber es ist eigentlich falsch:

serialEvent() ist eine Art ISR die vom System am Ende von loop() aufgerufen wird. Der ganze Sinn darin ist eben gerade dass man die Methode nicht per Hand aufrufen muss um zu fragen ob was da ist, sondern man weiß dann das mindestens ein char im Eingangspuffer ist. Deshalb braucht man da auch die globale Status-Variable, da die Funktion fest definiert ist und keinen Rückgabe-Wert hat.

Mit einem größerem Array hast du etwas Luft. Ist zwar nicht nötig solange die Indizes passen, aber ist sicherer :slight_smile:

So habe jetzt

PHsensorstring[3] = '\0';

und

serialEvent3();

raus der sketch sieht jetzt so aus und funktioniert auch so weit.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//PH
int indexPH;
char PH_Wert[5];
char PHsensorstring[5];
boolean PH_sensor_stringcomplete = false;

LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7);    //0x3F Test   //0x27

void setup()
{
  Serial.begin(38400); 
  Serial3.begin(38400);
  
  lcd.begin(20, 4);
  lcd.setBacklightPin(3,POSITIVE); 
  lcd.setBacklight(HIGH);
  lcd.clear(); 
  indexPH = 0;
}

void loop()
{
  Serial3.print("C\r");

  if (PH_sensor_stringcomplete)
  {
    Serial.println(PH_Wert);
    delay(50);
      
    lcd.setCursor(0, 0);
    lcd.print("PH");

    lcd.setCursor(3, 0);
    lcd.print(PH_Wert);

    indexPH = 0;
    PH_sensor_stringcomplete = false;
  }
      
}

void serialEvent3()
{
  char PH = (char)Serial3.read();

  if(PH == '\r')
  {
    PH_sensor_stringcomplete = true;
  }
  else
  {
    if(validateNumber(PH))
    {
      PH_Wert[indexPH] = PH;
      //PHsensorstring[3] = '\0';
      indexPH++;
    }
  }
}

bool validateNumber(char caracter)
{
  return ((caracter >= '0' && caracter <= '9') || caracter == '.');
}

Sieht ok aus :slight_smile:

Danke erst mal muß jetzt nur noch das mit den Kalibrierung über das Menü machen aber das sollte ich wohl ihn bekommen.