Author Topic: CO2 sensor hacking, MQTT messaging  (Read 20455 times)

Pegman

  • Newbie
  • *
  • Posts: 13
CO2 sensor hacking, MQTT messaging
« on: March 20, 2016, 03:38:26 am »
Some time ago I bought a Voltcraft CO2 sensor and after a spot of googling I found that it is not too difficult to tap into it and obtain CO2/temp/humidity measurements that way (reference).

My aim is to hook up the Oak to the Voltcraft, parse the measurements and send them to my Domoticz home domotica system, over MQTT (Mosquitto).

So far I have only a few explorations into the electronic part (figuring out which PCB connections to use, hooking up the logic analyzer), mostly waiting for the Oak to become 'mature'. But today I managed to hook up the Oak and send MQTT messages using the PubSubclient library.

I will share the MQTT connection code here, for others to use. Mind: this is not original work, it's an adaptation of a PubSubclient example, for use with the Oak. Basically removing Serial.print() statements and the WiFi initialization parts.

Code: [Select]
/*
 Basic ESP8266 MQTT example.

 20 Mar 2016 Adapted for use on Digistump Oak by Pegman, Digistump Forums.

 This sketch demonstrates the capabilities of the pubsub library in combination
 with the ESP8266 board/library.

 It connects to an MQTT server then:
  - publishes "hello world" to the topic "outTopic" every two seconds
  - subscribes to the topic "inTopic", printing out any messages
    it receives. NB - it assumes the received payloads are strings not binary
  - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
    else switch it off

 It will reconnect to the server if the connection is lost using a blocking
 reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
 achieve the same result without blocking the main loop.

*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>


const char* mqtt_server = "192.168.178.xxx";    // raspberrypi, Domoticz
const unsigned int mqtt_port = 11883;
const char* connection_id = "ESP8266Client";
const char* client_name = "digistumpoak";
const char* client_password = "secretpassword";


WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
}


void callback(char* topic, byte* payload, unsigned int length) {

  // Switch on the LED if an 1 was received as first character
  if ((char)payload[0] == '1') {
    digitalWrite(BUILTIN_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
    // but actually the LED is on; this is because
    // it is acive low on the ESP-01)
  } else {
    digitalWrite(BUILTIN_LED, HIGH);  // Turn the LED off by making the voltage HIGH
  }

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    // Attempt to connect
    if (client.connect(connection_id, client_name, client_password)) {
      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      // ... and resubscribe
      client.subscribe("inTopic");
    } else {
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  long now = millis();
  if (now - lastMsg > 2000) {
    lastMsg = now;
    ++value;
    snprintf (msg, 75, "hello world #%ld", value);
    client.publish("outTopic", msg);
  }
}

« Last Edit: March 20, 2016, 03:42:54 am by Pegman »

djflix

  • Newbie
  • *
  • Posts: 24
Re: CO2 sensor hacking, MQTT messaging
« Reply #1 on: March 22, 2016, 12:15:22 pm »
Sounds awesome! Is this a personal project or related to work or a commercial product? Keep up the good work  8)

defragster

  • Sr. Member
  • ****
  • Posts: 467
Re: CO2 sensor hacking, MQTT messaging
« Reply #2 on: March 22, 2016, 08:28:42 pm »
Thanks for sharing - MQTT is something I wanted to see work.  I got as far as installing a server on my Windows machine. Also got a $5 Pi Zero that might work as a server I'm hoping.

Pegman

  • Newbie
  • *
  • Posts: 13
Re: CO2 sensor hacking, MQTT messaging
« Reply #3 on: March 22, 2016, 11:30:26 pm »
I have Mosquitto running on my Pi 1. It does not have to do a lot of messaging but neither does it seem to cause a noticeable load increase.

Pegman

  • Newbie
  • *
  • Posts: 13
Re: CO2 sensor hacking, MQTT messaging
« Reply #4 on: March 22, 2016, 11:46:00 pm »
Sounds awesome! Is this a personal project or related to work or a commercial product? Keep up the good work  8)

Thanks! I've been thinking about it for a long time, finally the Oak is ready for use.
 It's a personal project. I /wish/ I could do that stuff for work  :-[

defragster

  • Sr. Member
  • ****
  • Posts: 467
Re: CO2 sensor hacking, MQTT messaging
« Reply #5 on: March 22, 2016, 11:46:43 pm »
That is promising if the Pi 1 can do it - the Pi Zero with updated horsepower/resources and draws less current.

I keep seeing cool Connectivity methods in various threads.  Starting one thread to track them might work but could get polluted quickly - it could become a WIKI section.  It occurred to me putting a unique string once per Thread where stuff works could at least allow a forum Search to work.
MQTT::OAK_CONNECT
« Last Edit: April 12, 2016, 05:18:35 pm by defragster »

Pegman

  • Newbie
  • *
  • Posts: 13
Re: CO2 sensor hacking, MQTT messaging
« Reply #6 on: April 16, 2016, 08:41:50 am »
Well, it happened  :)

I'm reading data from the Voltcraft CO-100 and sending it over MQTT to Domoticz, where it is turned into neat graphs. The cool thing is that there are no modifications needed to the Voltcraft device: there's a neat RJ45 socket that can be used for power and data, and the voltage levels are compatible with Oak.

The only thing that had me stumped for a day, coming from Arduino, is that the Oak requires the use of delay() here and there in order to stay connected to the WiFi  ::)

Flashing the Oak is still a pain: before each reflash I have to restart the Oak with pin 1 to GND, otherwise flashing invariably fails. I hope that will get sorted out at some point. Other than that: I love the little guy!

I made a little write up on my own site, with pictures and links to the code and examples on Github.




defragster

  • Sr. Member
  • ****
  • Posts: 467
Re: CO2 sensor hacking, MQTT messaging
« Reply #7 on: April 18, 2016, 01:11:58 am »
Hi @Pegman - looks like I cross posted my sample gathering - Project and Support - somehow I didn't see the MQTT was all from you :-)  Though I see you have different code there and here.

Can you sprinkle yield() in your code where you are busy (and not time critical) and see any change? That is what delay() does and it lets the WiFi hardware keep the device network active.

That delay(5000) in reconnect might stem from losing the network - and also end up keeping you out of loop() and causing your failure to get OTA particle uploads as well.

In reconnect() if the "while (!client.connected())" became an if() the delay(5000) could go away - that would get you back to loop where you could just exclude your code that needs "(client.connected()" and then watch millis() for 5 seconds to elapse between retries in loop() to reconnect.  Any time you can avoid delay() you will be better off. ... like you do with lastMsg.

Given the code you get to (given it is different in 2 places)- I might be inclined to try it myself if you can't clean it up to be more reliable.

Pegman

  • Newbie
  • *
  • Posts: 13
Re: CO2 sensor hacking, MQTT messaging
« Reply #8 on: April 18, 2016, 06:18:54 am »
Hi @Pegman - looks like I cross posted my sample gathering - Project and Support - somehow I didn't see the MQTT was all from you :-)  Though I see you have different code there and here.

The code on top, in this post, is a 'proof of concept' to see if the Oak would talk MQTT.
And it does, easily :)

Quote from: defragster
Can you sprinkle yield() in your code where you are busy (and not time critical) and see any change? That is what delay() does and it lets the WiFi hardware keep the device network active.

I'll give the yield() a try - probably close to the end of the work week because... work and stuff.

Quote from: defragster
That delay(5000) in reconnect might stem from losing the network - and also end up keeping you out of loop() and causing your failure to get OTA particle uploads as well.

In reconnect() if the "while (!client.connected())" became an if() the delay(5000) could go away - that would get you back to loop where you could just exclude your code that needs "(client.connected()" and then watch millis() for 5 seconds to elapse between retries in loop() to reconnect.  Any time you can avoid delay() you will be better off. ... like you do with lastMsg.

Given the code you get to (given it is different in 2 places)- I might be inclined to try it myself if you can't clean it up to be more reliable.

That's a good tip, I will try that to see if it improves the OTA uploads. As they are now, I always need physical access to the device and a temporary wire between pin 1 and GND :(

In general I do not like to use delay() anyway, unless when blinking LEDs ;)

defragster

  • Sr. Member
  • ****
  • Posts: 467
Re: CO2 sensor hacking, MQTT messaging
« Reply #9 on: April 18, 2016, 09:51:01 pm »
Hope yield() helps and avoiding long delay().  I'm not sure how the delay() in use is written - it may only yield once on entry or exit and then sit and wait in between.

delay(5000) might work better as a quick first step if this was used instead so a yield is triggered for sure while you wait?:

Code: [Select]
for ( int ii=0; ii<5000; ii++) {
    delay( 1 );
}

Pegman

  • Newbie
  • *
  • Posts: 13
Re: CO2 sensor hacking, MQTT messaging
« Reply #10 on: April 19, 2016, 06:29:26 am »
I got rid of most calls to delay(). There's only one left at the bottom of loop(), but that's just 50 ms.

Code: [Select]
  // Allow Oak to handle WiFi stuff in the loop()
  // while we're not busy reading data.
  if (! Voltcraft.readmore() ) {
    delay(PUBLISH_DELAY_MILLIS);
  }

yield() did not work here. I did not need to sprinkle any other calls to yield().

Your suggestion to remove the delay(5000) from the reconnect() function solved my OTA problem (*). I replaced it with a state, which is also more in line with the entire sketch:

Code: [Select]
    /* Reconnect to MQTT */
    case 100:
      if ( abs(millis() - previousReconnectAttempt) > DELAY_BETWEEN_CONNECTS_MILLIS) {  // non-blocking delay, we can still do stuff in loop()
        if (client.connect(connection_id, client_name, client_password)) {
          previousReconnectAttempt = millis();
          client.publish(statusTopic, "Oak has (re)connected");
          nextStatus = prevStatus;  // go back and attempt to publish our payload again
        }
        else {  // connect failed
          nextStatus = 0;  // give up, get new data, try again later
        }
      }
      break;

Works nicely, and is no longer blocking to loop(). Thanks for the suggestion!

(*) I am powering the Oak of the Voltcraft device and I think that it cannot provide enough power for the reflash: it fails all the time. OTA reflashing from USB power supply is fine.

defragster

  • Sr. Member
  • ****
  • Posts: 467
Re: CO2 sensor hacking, MQTT messaging
« Reply #11 on: April 19, 2016, 09:49:18 pm »
Good news on the success!  That says that the delay(5000) either wasn't yielding much - or it needs to return to and exit loop() for real particle processing. [if that is the case the for(ii<1000){yield(5)} wouldn't have helped]  I didn't see any other long delay() - but wasn't sure I read all the code so just added the 'yield() more' in case I missed something.

I set up my first ESP8266 on a breadboard that gave weak power to the ESP8266 - it thought it was flashing each time - but never booted - tried ESP's "AT" - then LUA - then Arduino before I saw voltage was at like 2.5.  It would go through the steps but not 'burn' the new code.

I doubled up my GND/3.3V lines across the breadboard from the same source and it started working - and I was glad I ended up on Arduino as it is more familiar and functional for my purposes.

Pegman

  • Newbie
  • *
  • Posts: 13
Re: CO2 sensor hacking, MQTT messaging
« Reply #12 on: April 20, 2016, 10:44:39 pm »
Arduino also allows one to have direct Serial output. I like the idea of Oak talking to particle.io and all, but direct Serial is easier and quicker.

I am leeching power off the device, can't improve on that without mechanical intervention. But it's OK, once the code is 'gold' I don't feel the need to tweak it much anymore. Either in-place or tethered :D

saperlot

  • Newbie
  • *
  • Posts: 16
Re: CO2 sensor hacking, MQTT messaging
« Reply #13 on: June 07, 2016, 10:17:16 am »
Hello
I have something to share which is related to that.

It is also easily possible to integrate a RGB led control over OpenHAB which can look like this:
Picture taken from: https://github.com/openhab/openhab/wiki/Hue-Binding


You can build your own Hue. ;-)

This example uses the RGB shield.
After some trial and error lessons with the delays, it works like a charm.

Library taken from http://pubsubclient.knolleary.net/

The code is just quick and dirty, but is maybe helpful for others:
Code: [Select]
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <string.h>

// Update these with values suitable for your network.

const char* mqtt_server = "....................";

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];


int value = 0;

int ledpin_r = 0;
int ledpin_g = 1;
int ledpin_b = 2;

long lastReconnectAttempt = 0;

void setup() {
  pinMode(ledpin_r, OUTPUT);     
  pinMode(ledpin_g, OUTPUT);
  pinMode(ledpin_b, OUTPUT);

  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
    lastReconnectAttempt = 0;
}

void callback(char* topic, byte* payload, unsigned int length) {
  unsigned int temp_r, temp_g, temp_b;
  char temparray[12];
  char *endptr;
 
  for (int i = 0; i<12;i++){
  temparray[i] = payload[i];
  }
 
  temp_r = strtoul(temparray,&endptr,10);
  endptr++;
  temp_g = strtoul(endptr,&endptr,10);
  endptr++;
  temp_b = strtoul(endptr,&endptr,10);

  analogWrite(ledpin_r,temp_r*4);
  analogWrite(ledpin_g,temp_g*4);
  analogWrite(ledpin_b,temp_b*4);

}

boolean reconnect() {
  if (client.connect("OAK")) {
    // Once connected, publish an announcement...
    client.publish("outTopic/stat","hello world");
    // ... and resubscribe
    client.subscribe("inTopic/RGB/color");
  }
  return client.connected();
}

void loop() {
  long now = millis();
  if (!client.connected()) {
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      // Attempt to reconnect
      if (reconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  } else {
    client.loop();   
  }
  delay(20);
}


And some OpenHAB configurations:

items:
Code: [Select]
Color RGBLed "RGB Color" (All)
String RGBLedColor (All) {mqtt=">[mosquitto:inTopic/RGB/color:command:*:default]"}

sitemap:
Code: [Select]
Frame label="RGBLed" {
Colorpicker item=RGBLed icon="slider"
}