DHT22 code with MQTT

hey

starting out with mqtt and arduino. Decided to delve in and try out a DHT22 sensor as I have an application that best suits this sensor to any other due to its temperature ranges and accuracy.

Using some examples from the web, and a bit of fiddling, I managed to get MQTT working with an alternative sensor. I then checked with MQTTlens and had a good constant stream of data :slight_smile:

Changing the code slowly, bit by bit, I added code from the DHT22 library which has been posted on the arduino site.

Unfortunately I am now only receiving an initial MQTT post to say that the device is online, and then a single post with the message '1' for temperature and and no post for humidity.

Would someone mind looking at my code and pointing out where my code is wrong please?

Also, I've previously struggled posting digital output commands to the arduino. Anyone able to post an example?

Many thanks

//   ARDUINO 01 - Loft

// All includes
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <dht.h>

// Ethernet Information
byte mac[]    = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };  
byte server[] = { 172, 16, 1, 230 };  
byte ip[]     = { 172, 16, 1, 235 }; 

// All definitions

// --DHT--
dht DHT;
#define DHT22_PIN 6

// --Other Temp Sensor---
int sensorPin=5;
int temperature = (DHT.temperature, 1);
int humidity = (DHT.humidity, 1);
int lastTemperature;
int lastHumidity;
unsigned long lastTime;
char buffer[10];

// Read MQTT messages
void callback(char* topic, byte* payload, unsigned int length) {
  // handle message arrived (no messages expected though)
}


// Setup Ethernet Client to talk to MQTT broker
EthernetClient ethClient;  
PubSubClient client(server, 1883, callback, ethClient);

void setup() {  
  Ethernet.begin(mac, ip);
  if (client.connect("Arduino 01 (235) Loft")) {
    client.publish("Ourplace/Nodes/Arduino-01", "Online");
    lastTemperature = 0;
    lastHumidity = 0;
    lastTime = 0;
  }
}

void loop() {  
  // Other Temp Sensor settings
  int reading = analogRead(sensorPin);
  int temperature = ((reading * 0.004882) - 0.50) * 100;
  //if (temperature != lastTemperature) {
  //  if (millis()>(lastTime+1000)) {
  //    sprintf(buffer,"%d",temperature);
  //    client.publish("Ourplace/Loft/Temperature", buffer);
  //    lastTemperature = temperature;
  //    lastTime = millis();
  //  }
//  }
  // DHT22 Sensor
  // --Read Data--
  int chk = DHT.read22(DHT22_PIN);
  switch (chk)
  if (temperature != lastTemperature) {
    if (millis()>(lastTime+1000)) {
      sprintf(buffer,"%d",temperature);
      client.publish("Ourplace/Loft/Temperature", buffer);
      lastTemperature = temperature;
      lastTime = millis();
    }
  }
  if (humidity != lastHumidity) {
    if (millis()>(lastTime+1000)) {
      sprintf(buffer,"%d",humidity);
      client.publish("Ourplace/Loft/Temperature", buffer);
      lastHumidity = humidity;
      lastTime = millis();
    }
  }

  
  client.loop();
}
int temperature = (DHT.temperature, 1);

?

switch (chk)

is used to test the output of the read(). You are not doing that.

Hi ajheyworth

Also, I've previously struggled posting digital output commands to the arduino. Anyone able to post an example?

Do you mean you want the Arduino to subscribe to a topic and then set digital output pin levels according to messages received?

Regards

Ray

Hackscribble:
Hi ajheyworth

Do you mean you want the Arduino to subscribe to a topic and then set digital output pin levels according to messages received?

Regards

Ray

Hi Ray

That's exactly what I'm looking to do.

Regards

robtillaart:
switch (chk)

is used to test the output of the read(). You are not doing that.

Hi Rob

I do have

int chk = DHT.read22(DHT22_PIN);
switch (chk)

within the code under the DHT22 Sensor section. I thought this was in the right place? So it reads then runs through IF conditions depending on the reading.

Could you suggest a better way to write this or better location for this code? Or is it incorrect altogether?

Many thanks.

AWOL:

int temperature = (DHT.temperature, 1);

?

Dave

Using the DHT library and associated web page and code as a guide, I was under the impression that DHT.temperature was the element associated with the temperature value read by the DHT22 device. "-"DHT.temperature, 1" is to one decimal place and "temperature" being the alias for this within my code.

Could be wrong.

Here is the code I used as a guide:

http://playground.arduino.cc/Main/DHTLib

Thanks

ajheyworth:
Hi Ray

That's exactly what I'm looking to do.

Regards

OK, I use a different version of PubSubClient so have not been able to test this with an MQTT feed.

As a simple example, to send messages to the topic "Ourplace/Loft/Outputs" where the messages are in the format "13,HIGH" or "7,LOW" (i.e. pin number,level - case sensitive) ...

Declare some globals:

#define SUB_TOPIC "Ourplace/Loft/Outputs"
#define MAX_RECEIVE_PAYLOAD 20
#define MAX_LEVEL_LENGTH 5
char receiveBuffer[MAX_RECEIVE_PAYLOAD];

In setup(), add the pinMode calls for the pins you are using and, after the client connects, add:

client.subscribe(SUB_TOPIC);

Replace the empty callback function with this:

void callback(char* topic, byte* payload, unsigned int length)
{
  byte numBytes = min(length, MAX_RECEIVE_PAYLOAD);  // avoid overflow of receiveBuffer
  strncpy(receiveBuffer, (const char*)payload, numBytes);
  receiveBuffer[numBytes] = '\0';  // make it a C string
  if (strstr(topic, SUB_TOPIC))  // ignore messages for other topics
  {
    // look for command with format "<pin-number>,<level>" e.g. "13,HIGH" - case sensitive!
    byte pinNumber;
    char level[MAX_LEVEL_LENGTH];
    char * partOfString;  // pointer moves along string with each strtok call
    partOfString = strtok(receiveBuffer, ",");  // look for comma as separator
    pinNumber = atoi(partOfString);  // NO VALIDITY CHECKING!
    partOfString = strtok(NULL, ","); // in this case, this should find end of string, not another comma
    strcpy(level, partOfString);
    if (strstr(level, "HIGH"))
    {
      digitalWrite(pinNumber, HIGH);
    }
    else if (strstr(level, "LOW"))
    {
      digitalWrite(pinNumber, LOW);
    }
  }
}

When the library received a message, it calls the callback function, passing it the topic (you may in future subscribe to multiple topics), the payload data and the length of the data (since it may not be a null-terminated C string).

Could be wrong

Yes, you are.
All that line does is set "temperature" to 1.

Who is Dave?

Hello Ray (hackscribble)

I've followed your steps and it worked brilliantly, thank you.

If you don't mind, I need to now get the arduino so that it responds purely to ON and OFF messages to a particular topic. For instance, digital pin 1 = topic 1, pin 2 = topic 2 etc. The reason for this is that I'm connecting to OpenHAB and its commands can only be ON or OFF.

Many thanks in advance for your help.

Regards

AWOL

Sorry, I must have read Dave somewhere associating it for some reason with your thread. Mistake.

Thanks for pointing out the DHT.temperature, 1

Are you able to assist any further?

If you don't mind, I need to now get the arduino so that it responds purely to ON and OFF messages to a particular topic. For instance, digital pin 1 = topic 1, pin 2 = topic 2 etc. The reason for this is that I'm connecting to OpenHAB and its commands can only be ON or OFF.

So each topic title relates to a specific pin and the message content is either "ON" or "OFF"?

Under OpenHAB, do you have freedom to specify the topic title format? For example, "Ourplace/Loft/P07" where "P07" means pin 7?

Hi Ray

Understood, and yes I can do that.

Change the definitions. I've assumed the pins to be controlled will be in one contiguous range.

#define HAB_TOPIC_PREFIX "Ourplace/Loft/P"
#define HAB_TOPIC_TOKEN "/P"
#define FIRST_PIN 7
#define LAST_PIN 13
#define MAX_TOPIC_LENGTH 20
#define MAX_RECEIVE_PAYLOAD 5
char receiveBuffer[MAX_RECEIVE_PAYLOAD];

In setup(), change the subscribe() calls to the topics you need.

char temp[MAX_TOPIC_LENGTH];
for (byte i = FIRST_PIN; i <= LAST_PIN; i++)
{
  sprintf(temp, "%s%02d", HAB_TOPIC_PREFIX, i);
  client.subscribe(temp);
}

Change the callback function. It ignores any topics that don't begin "Ourplace/Loft/P". Then it extracts the pin number after the ".../P", checks that it is in the allowed range and sets the level according to what's in the message.

void callback(char* topic, byte* payload, unsigned int length)
{
  if (strstr(topic, HAB_TOPIC_PREFIX))
  {
    char * partOfString;  // pointer moves along string with each strtok call
    partOfString = strtok(topic, HAB_TOPIC_TOKEN);  // find the token just before the pin number
    partOfString = strtok(topic, ",");  // move to last part of topic
    byte pinNumber = atoi(partOfString);  
    if ((pinNumber >= FIRST_PIN) && (pinNumber <= LAST_PIN))
    {
      byte numBytes = min(length, MAX_RECEIVE_PAYLOAD);  // avoid overflow of receiveBuffer
      strncpy(receiveBuffer, (const char*)payload, numBytes);
      receiveBuffer[numBytes] = '\0';  // make it a C string
      if (strstr(receiveBuffer, "ON"))
      {
        digitalWrite(pinNumber, HIGH);
      }
      else if (strstr(receiveBuffer, "OFF"))
      {
        digitalWrite(pinNumber, LOW);
      }
    }
  }
}

Hi Ray

Thank you.

char temp[MAX_TOPIC_LENGTH];
for (byte i = FIRST_PIN; i <= LAST_PIN; i++)
{
  sprintf(temp, "%s%02d", HAB_TOPIC_PREFIX, i);
  client.subscribe(temp);
}

Does this mean to replace all instances of temp in the above code and create a code structure for each topic, or just in the line client.subscribe(temp); and replicate like this:

client.subscribe(Loft);
client.subscribe(Kitchen);

Also I noticed that you said in the callback function "It ignores any topics that don't begin "Ourplace/Loft/P""

Bearing in mind the above Loft, Kitchen topics, I'm not completely understanding this.

Sorry!

Does this mean to replace all instances of temp in the above code and create a code structure for each topic, or just in the line client.subscribe(temp); and replicate like this:

client.subscribe(Loft);
client.subscribe(Kitchen);

Also I noticed that you said in the callback function "It ignores any topics that don't begin "Ourplace/Loft/P""

Bearing in mind the above Loft, Kitchen topics, I'm not completely understanding this.

I think my choice of topic name has confused things :slight_smile:

My code uses one topic prefix to identify commands to set the outputs on this one Arduino. You could use the prefix of just "Ourplace/P". So if you needed to control three output pins on this Arduino (from pin 8 to pin 10), the topics would be "Ourplace/P08", "Ourplace/P09" and "Ourplace/P10".

The code you quoted in reply #14 generates these three topic names and subscribes to them. It saves you having to type out lots of subscribe statements - not a problem with three but a bit of a pain if you had lots. You just include that code "as is" once in setup and set the #defines for the first pin and last pin. The #defines are also used to validate the pin number you receive.

Good morning Ray

Thank you for the clarification.

I've copied and pasted your code 'as is', just changing the first and last pin numbers to:

#define FIRST_PIN 3
#define LAST_PIN 6

Unfortunately when I send the ON command to topic Ourplace/Loft/P03 from either OpenHAB or MQTTLens (I use for message testing/diagnostics), there is no output from the arduino.

Your initial code which just needed a change in the topic handling did work from MQTTLens.

Aaron

Can you post the version of the code which isn't working? I'll take a look at it.

Sure. Thank you. Apologies for the general messy state of the rest of the code.

//   ARDUINO 01 - Loft

// All includes
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <dht.h>
#include "Wire.h"

// Ethernet Information
byte mac[]    = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };  
byte server[] = { 172, 16, 1, 230 };  
byte ip[]     = { 172, 16, 1, 235 }; 

// All definitions

// --Topics for receiving MQTT commands---
#define HAB_TOPIC_PREFIX "Ourplace/Loft/P"
#define HAB_TOPIC_TOKEN "/P"
#define FIRST_PIN 3
#define LAST_PIN 6
#define MAX_TOPIC_LENGTH 20
#define MAX_RECEIVE_PAYLOAD 5
char receiveBuffer[MAX_RECEIVE_PAYLOAD];

// --MQTT Digital Outputs
 int D2 = 2;
 int D3 = 3;
 int D4 = 4;
 int D5 = 5;
 
// --TMP102--
#define TMP102_I2C_ADDRESS 72 /* This is the I2C address for our chip. */

// --DHT--
dht DHT;
#define DHT22_PIN 6

// --Other Temp Sensor---
int sensorPin=5;
int temperature = (DHT.temperature, 1);
int humidity = (DHT.humidity, 1);
int lastTemperature;
int lastHumidity;
unsigned long lastTime;
char buffer[10];

// Read MQTT messages
void callback(char* topic, byte* payload, unsigned int length)
{
  if (strstr(topic, HAB_TOPIC_PREFIX))
  {
    char * partOfString;  // pointer moves along string with each strtok call
    partOfString = strtok(topic, HAB_TOPIC_TOKEN);  // find the token just before the pin number
    partOfString = strtok(topic, ",");  // move to last part of topic
    byte pinNumber = atoi(partOfString);  
    if ((pinNumber >= FIRST_PIN) && (pinNumber <= LAST_PIN))
    {
      byte numBytes = min(length, MAX_RECEIVE_PAYLOAD);  // avoid overflow of receiveBuffer
      strncpy(receiveBuffer, (const char*)payload, numBytes);
      receiveBuffer[numBytes] = '\0';  // make it a C string
      if (strstr(receiveBuffer, "ON"))
      {
        digitalWrite(pinNumber, HIGH);
      }
      else if (strstr(receiveBuffer, "OFF"))
      {
        digitalWrite(pinNumber, LOW);
      }
    }
  }
}


// Setup Ethernet Client to talk to MQTT broker
EthernetClient ethClient;  
PubSubClient client(server, 1883, callback, ethClient);

void setup() {
  Wire.begin(); // start the I2C library
  Ethernet.begin(mac, ip);
  if (client.connect("Arduino 01 (235) Loft")) {
    client.publish("Ourplace/Nodes/Arduino-01", "Online");
    lastTemperature = 0;
    lastHumidity = 0;
    lastTime = 0;
  }
 pinMode (D2, OUTPUT);
 pinMode (D3, OUTPUT);
 pinMode (D4, OUTPUT);
 pinMode (D5, OUTPUT);
 
 char temp[MAX_TOPIC_LENGTH];
for (byte i = FIRST_PIN; i <= LAST_PIN; i++)
{
  sprintf(temp, "%s%02d", HAB_TOPIC_PREFIX, i);
  client.subscribe(temp);
}
}

void getTemp102(){
    byte firstbyte, secondbyte; //these are the bytes we read from the TMP102 temperature registers
    int val; // an int is capable of storing two bytes, this is where we "chuck" the two bytes together.
     float convertedtemp; // We then need to multiply our two bytes by a scaling factor, mentioned in the datasheet.
     float correctedtemp;  //the sensor overreads
     Wire.beginTransmission(TMP102_I2C_ADDRESS); //Say hi to the sensor.
     Wire.write(0x00);
     Wire.endTransmission();
     Wire.requestFrom(TMP102_I2C_ADDRESS, 2);
     Wire.endTransmission();
     firstbyte      = (Wire.read());
     secondbyte     = (Wire.read());
     val = ((firstbyte) << 4);  // MSB
     val |= (secondbyte >> 4);  // LSB
     convertedtemp = val*0.0625;
     correctedtemp = convertedtemp - 5;
     sprintf(buffer,"%d",correctedtemp);
     client.publish("Ourplace/Loft/Temperature", buffer);
}

void loop() {  
  // Other Temp Sensor settings
  int reading = analogRead(sensorPin);
  int temperature = ((reading * 0.004882) - 0.50) * 100;
  //if (temperature != lastTemperature) {
  //  if (millis()>(lastTime+1000)) {
  //    sprintf(buffer,"%d",temperature);
  //    client.publish("Ourplace/Loft/Temperature", buffer);
  //    lastTemperature = temperature;
  //    lastTime = millis();
  //  }
//  }
  // TMP102 Sensor
  getTemp102();
  delay(5000);
  // DHT22 Sensor
  // --Read Data--
  int chk = DHT.read22(DHT22_PIN);
  switch (chk)
  if (temperature != lastTemperature) {
    if (millis()>(lastTime+1000)) {
      sprintf(buffer,"%d",temperature);
      //client.publish("Ourplace/Loft/Temperature", buffer);
      lastTemperature = temperature;
      lastTime = millis();
    }
  }
  if (humidity != lastHumidity) {
    if (millis()>(lastTime+1000)) {
      sprintf(buffer,"%d",humidity);
      client.publish("Ourplace/Loft/Temperature", buffer);
      lastHumidity = humidity;
      lastTime = millis();
    }
  }

  
  client.loop();
}

Apologies, Aaron. Found a couple of bugs in my code in reply #13.

Change these #defines:

#define HAB_TOPIC_PREFIX "Ourplace/Loft/=
#define HAB_TOPIC_TOKEN "="

Please use topic names in the format "Ourplace/Loft/=03" instead of ".../P03" and avoid using "=" anywhere else in the topic name. I believe that "=" is not an MQTT wildcard character, so should work OK.

In the callback function, change the second of these two statements as shown:

    partOfString = strtok(topic, HAB_TOPIC_TOKEN);  // find the token just before the pin number
    partOfString = strtok(NULL, ",");  // move to last part of topic

Also, although not part of the problem, move this code so it is inside the if client.connect() statement:

char temp[MAX_TOPIC_LENGTH];
for (byte i = FIRST_PIN; i <= LAST_PIN; i++)
{
  sprintf(temp, "%s%02d", HAB_TOPIC_PREFIX, i);
  client.subscribe(temp);
}

And your pinMode statements have a different range of pin numbers from the #defines.