Grove Kohlenstoffdioxid Sensor mit der senseBox


#1

Hallo zusammen,
Im folgenden stelle ich einmal eine Lösung vor wie man den Kohlenstoffdioxid Sensor von Groove an die senseBoxMCU anschliessen kann.

Material

Hardware

Der Kohlenstoffdioxid Sensor verwendet das Grove System . Das Grove System wird nicht von der senseBoxMCU unterstützt, deswegen muss das Grove Kabel an ein JST Kabel der senseBox gelötet werden. Tabelle 1 zeigt die PIN Belegung beim Grove und JST System. Die Kabel sollten wie in der Tabelle aufgezeigt gelötet und anschließend entsprechend isoliert werden (Gelb an Grün, Weiß an Gelb, usw.). Nachfolgend kann das JST-Ende des Kabels an einen seriellen Port der senseBox und das Grove-Ende an den Sensor angeschlossen werden.

Grove JST(senseBox)
Gelb(PIN1) Grün (Rx)
Weiß(PIN2) Gelb(Tx)
Rot(PIN3) Rot(5V)
Schwarz(PIN4) Schwarz(GND)

Software

Anfangs deklarieren wir eine DEBUG Methode mit der wir durch #define ENABLE_DEBUG Konsolenausgaben an oder ausschalten können.

// Uncomment the next line to get debugging messages printed on the Serial port
// Do not leave this enabled for long time use
#define ENABLE_DEBUG
#ifdef ENABLE_DEBUG
#define DEBUG(str) Serial.println(str)
#else
#define DEBUG(str)
#endif

Um den Sensor ans Laufen zu bringen ist keine zusätzliche Library nötig.
Als erstes muss der Serielle Port an dem der Sensor angeschlossen ist definiert werden. Anschließend wird eine Variable für den Zugriff auf den Sensor gespeichert.

#define GROVE_CO2(Serial2)
const unsigned char cmd_get_sensor[] = {0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x79 };

Darüber hinaus deklarieren wir noch eine Buffer Variable wenn der Sensor Daten liefert und 2 Variablen in denen wir die Messwerte speichern.

unsigned char dataReceived[9];
int temperature_co2;
int CO2PPM;

Nun sind alle Variablen im Header definiert. Nun muss noch eine Helfer Funktion deklariert werden, welche die Sensorwerte ausliest und in den eben angelegten Variablen speichert.

// Helper function for co2 sensor
int dataReceive(void)
{
 byte data[9];
 int i = 0;
 //transmit command data
 for(i=0; i<sizeof(cmd_get_sensor); i++)
 {
     GROVE_CO2.write(cmd_get_sensor[i]);
 }
 delay(10);
 //begin receiving data
 if(GROVE_CO2.available())
 {
    while(GROVE_CO2.available())
    {
        for(int i=0;i<9; i++)
        {
            data[i] = GROVE_CO2.read();
        }
    }
}
delay(100);
if((i != 9) || (1 + (0xFF ^ (byte)(data[1] + data[2] + data[3] + data[4] + data[5] + data[6] + data[7]))) !=   data[8])
{
    return false;
}
CO2PPM = (int)data[2] * 256 + (int)data[3];
temperature_co2 = (int)data[4] - 40;
return CO2PPM;
}

Damit der Sensor am Anfang des Programms gestartet werden soll muss der entsprechende Befehl noch in die Setup-Funktion eingefügt werden.

void setup(){
   GROVE_CO2.begin(9600);
  }

Damit ist die Konfigurierung abgeschlossen und der Sensor kann in der Loop-Funktion wiefolgt ausgelesen werden.

void loop(){
  uint8_t attempt_co2 = 0;
  while (attempt_co2 < 5) {
    bool error_co2 = dataReceive();
    if (error_co2 != 0) {
      DEBUG(F("CO2 PPM: "));
      DEBUG(CO2PPM);
      break;
    }
    attempt++;
  }

Der Messwert für CO2 wird nun nach jedem Loop Vorgang in der Variable

CO2PPM 

gespeichert.

Sonstiges

Ein Beispiel wie ein fertiger Sketch der per LoRaWAN CO2 Werte an die openSenseMap schickt habe ich hier hochgeladen. Die zugehörige Payload Funktion ist hier.

Manchmal können die Farben der Grove Kabel sich von meiner Tabelle unterscheiden. Hierfür am besten noch diese Website beachten.


#2

Hallo,

ein schönes Beispiel mit ein ausgezeichneter Doku, verführt zum Nachbau.
:grin:


#3

Hallo @Thiemann96,

habe aus den Codestücken ein Programm zusammengesetzt zum Testen zusammen gesetzt, erhalte aber diverse Fehlermeldungen;
‘’’
Arduino: 1.8.7 (Mac OS X), Board: “senseBox MCU, ON as Default”

CO2-Test:6:28: error: ‘unsigned char dataReceive [9]’ redeclared as different kind of symbol
unsigned char dataReceive[9];
^
/Users/rolf/Documents/Arduino/CO2-Test/CO2-Test.ino:11:5: note: previous declaration ‘int dataReceive()’
int dataReceive(void)
^~~~~~~~~~~
/Users/rolf/Documents/Arduino/CO2-Test/CO2-Test.ino: In function ‘int dataReceive()’:
CO2-Test:18:6: error: ‘GROVE_CO2’ was not declared in this scope
GROVE_CO2.write(cmd_get_sensor[i]);
^~~~~~~~~
CO2-Test:22:5: error: ‘GROVE_CO2’ was not declared in this scope
if(GROVE_CO2.available())
^~~~~~~~~
/Users/rolf/Documents/Arduino/CO2-Test/CO2-Test.ino: In function ‘void setup()’:
CO2-Test:43:2: error: ‘GROVE_CO2’ was not declared in this scope
GROVE_CO2.begin(9600);
^~~~~~~~~
/Users/rolf/Documents/Arduino/CO2-Test/CO2-Test.ino: In function ‘void loop()’:
CO2-Test:52:7: error: ‘DEBUG’ was not declared in this scope
DEBUG(F("CO2 PPM: "));
^~~~~
CO2-Test:56:5: error: ‘attempt’ was not declared in this scope
attempt++;
^~~~~~~
/Users/rolf/Documents/Arduino/CO2-Test/CO2-Test.ino:56:5: note: suggested alternative: ‘mktemp’
attempt++;
^~~~~~~
mktemp
CO2-Test:57:1: error: expected ‘}’ at end of input
}
^
exit status 1
‘unsigned char dataReceive [9]’ redeclared as different kind of symbol

Dieser Bericht wäre detaillierter, wenn die Option
“Ausführliche Ausgabe während der Kompilierung”
in Datei -> Voreinstellungen aktiviert wäre.
‘’’

Für mich sieht es so aus als würde er doch eine Library GROVE_CO2 suchen.
Habe ich was übersehen?
Außerdem habe ich im Vergleich mit dem LoRA-Programm festgestellt, dass die folgende Zeile eine andere Variable enthält:

unsigned char dataRevice[9];

ist das ein Schreibfehler oder Absicht?


#4

Hallo @altprog,

Die “Grove” Library wird in der ersten Zeile mit
#define GROVE_CO2(Serial2)
deklariert.

Zudem habe ich gemerkt, dass sich ein kleiner Copy&Paste Fehler eingeschlichen hat, denn die DEBUG Methode ist nicht implementiert. Dies muss wie die Initialisierung der Grove Library im Header passieren.

#ifdef ENABLE_DEBUG
#define DEBUG(str) Serial.println(str)
#else
#define DEBUG(str)
#endif

Letzlich hat sich wie du schon richtig vermutet hast ein Fehler bei der Deklaration von unsigned char dataRevice[9]; eingeschlichen. Diese Variable muss mit:
unsigned char dataReceived[9];
deklariert werden.
Ich hab die Fehler im Eingangs-Post entsprechend korriegiert.

Diese Fehlermeldung müsstest du in deinem Programmcode ändern:

Die Variable attempt scheinst du nicht eingangs deklariert zu haben das kannst du mit uint8_t attempt = 0; beheben.

CO2-Test:56:5: error: ‘attempt’ was not declared in this scope
attempt++;
^~~~~~~
/Users/rolf/Documents/Arduino/CO2-Test/CO2-Test.ino:56:5: note: suggested alternative: ‘mktemp’
attempt++;
^~~~~~~
mktemp

Hier hast du eine schließende Klammer in deinem Programmcode vergessen bzw. eine zu viel gemacht.

CO2-Test:57:1: error: expected ‘}’ at end of input
}
^

Entschuldige für die eingeschlichenen Fehler und ich hoffe es funktioniert jetzt bei dir. Würde mich über eine Rückmeldung freuen :grinning:


#5

@Thiemann96

Hallo,

dank für die ausführliche Antwort. Die Fehler werde ich korrigieren, nur eins habe ich nicht verstanden, Du schreibst:

Blockquote
Um den Sensor ans Laufen zu bringen ist keine zusätzliche Library nötig.

Du schreibst aber in Deiner letzten Antwort:

Blockquote

Die “Grove” Library wird in der ersten Zeile mit
#define GROVE_CO2(Serial2)
deklariert.

Also NEHME ICH AN GROVE_CO2 ist eine Library.
Frage: wo finde ich diese Library?
Ich habe bei SEED Studio nachgesehen aber nichts gefunden.

Herzliche Grüße

Altprog


#6

Hallo zusammen,

ich habe das Programm von Thiemann96 so gut es gehr debuggt aber eine Fehlermeldung bleibt hartnäckig:
Nämlich:

/Users/rolf/Documents/Arduino/CO2-Test/CO2-Test.ino: In function ‘int dataReceive()’:
CO2-Test:30:6: error: ‘GROVE_CO2’ was not declared in this scope
GROVE_CO2.write(cmd_get_sensor[i]);
^~~~~~~~~

‘’’
/* Programm zum Testen des CO2 Sensors

  • Version 0.1 vom 31.10.2019
    */

// Uncomment the next line to get debugging messages printed on the Serial port
// Do not leave this enabled for long time use
#define ENABLE_DEBUG
#ifdef ENABLE_DEBUG
#define DEBUG(str) Serial.println(str)
#else
#define DEBUG(str)
#endif

#include <SPI.h>
#include <senseBoxIO.h>
#define GROVE_CO2(Serial2)
const unsigned char cmd_get_sensor[] = {0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x79 };
unsigned char dataReviced[9];
int temperature_co2;
int CO2PPM;

// Helper function for co2 sensor
int dataReceive(void)
{
byte data[9];
int i = 0;
//transmit command data
for(i=0; i<sizeof(cmd_get_sensor); i++)
{
GROVE_CO2.write(cmd_get_sensor[i]);
}
delay(10);
//begin receiving data
if(GROVE_CO2.available())
{
while(GROVE_CO2.available())
{
for(int i=0;i<9; i++)
{
data[i] = GROVE_CO2.read();
}
}
}
delay(100);
if((i != 9) || (1 + (0xFF ^ (byte)(data[1] + data[2] + data[3] + data[4] + data[5] + data[6] + data[7]))) != data[8])
{
return false;
}
CO2PPM = (int)data[2] * 256 + (int)data[3];
temperature_co2 = (int)data[4] - 40;
return CO2PPM;
}

void setup() {
// put your setup code here, to run once:
GROVE_CO2.begin(9600);
}

void loop() {

uint8_t attempt = 0;
while (attempt < 5) {
bool error_co2 = dataReceive();
if (error_co2 != 0) {
DEBUG(F("CO2 PPM: "));
DEBUG(CO2PPM);
break;
}
attempt++;
}
}
‘’’


#7

Füge mal ein Leerzeichen vor der Klammer ein, dann sollte es funktionieren :slight_smile:

Es handelt sich hierbei nicht um eine Library, sondern es wird nur die Konstante “GROVE_CO2” mit dem Wert “Serial2” definiert.

Beste Grüße
Mario


#8

Hi @mario,

danke für den Tipp!:joy:
Hat funktioniert.
Aber zur Erklärung: Wenn “GROVE_CO2” nur eine Konstante ist, wieso hat sie dann u.A. die Methode “GROVE_CO2.write” oder “GROVE_CO2.available”?


#9

Weil der Wert der Konstante Serial2 ist und somit alle Funktionen, die “Serial” bereitstellt (siehe https://www.arduino.cc/reference/en/language/functions/communication/serial/) , verwendet werden können.

Anstatt GROVE_CO2 könntest du auch überall Serial/Serial2 (je nach Anschluss) hinschreiben.


#10

@mario

danke für die Klarstellung.
:smiley: