2
\$\begingroup\$

Note: This might not be a programming question. If it turns out that it is, I will close it.

I am using an ESP8266 to build some air quality web servers. One of the sensors I am using is the DHT11 (temperature and humidity). I have some working code that relies on global variables and I want to convert it into code that does not use globals. Initially I just rewrote everything using code that, as far as I can tell, is perfectly valid. Then the sensor did not work. So I decided to modify the working code step by step to see which change causes it to fail. The code failed on the very first change!

I am no C expect, but what I have done seems pretty innocuous. For this reason, I don't think this is a programming question, but rather a question about some (unknown to me) quick with the DHT11.

Working code:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h> 
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <dht_nonblocking.h>
#define DHT_SENSOR_TYPE DHT_TYPE_11

ESP8266WiFiMulti wifiMulti; 
ESP8266WebServer server(80);

static const int DHT_SENSOR_PIN = 12;
DHT_nonblocking dht_sensor( DHT_SENSOR_PIN, DHT_SENSOR_TYPE );

static float global_temp;
static float global_humidity;
static unsigned long global_dht_measure_time = millis( );

static bool measure_environment( float *temperature, float *humidity )
{
  /* Measure once every 3 seconds. */
  if( millis( ) - global_dht_measure_time > 3000ul )
  {
    if( dht_sensor.measure( temperature, humidity ) == true )
    {
      global_dht_measure_time = millis( );
      return( true );
    }
  }

  return( false );
}

String prepareJsonObject()
{

  String res =
    String("{") +
    "\"Temperature\": " + String(global_temp) + "," +
    "\"Humidity\": " + String(global_humidity) + "," +
    "\"MeasureTime\": " + String(millis( ) - global_dht_measure_time) +
    "}";

  return res;
}

void handleRoot() {
  Serial.println("Handling Root");
  server.send(200, "application/json", prepareJsonObject());
}

void handleNotFound(){
  server.send(404, "text/plain", "404: The requested page was not found");
}

void setup(void){
  Serial.begin(115200);
  delay(10);
  Serial.println('\n');

  wifiMulti.addAP("ssid", "***");

  Serial.println("Connecting ...");
  int i = 0;
  while (wifiMulti.run() != WL_CONNECTED) {
    delay(250);
    Serial.print('.');
  }
  Serial.println('\n');
  Serial.print("Connected to ");
  Serial.println(WiFi.SSID()); 
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("AirSensor1")) {
    Serial.println("mDNS responder started");
  } else {
    Serial.println("Error setting up MDNS responder!");
  }

  server.on("/", HTTP_GET, handleRoot);
  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void){
//  Serial.println("Entering loop");
  if( measure_environment( &global_temp, &global_humidity ) == true )
  {
    Serial.print( "T = " );
    Serial.print( global_temp, 1 );
    Serial.print( " deg. C, H = " );
    Serial.print( global_humidity, 1 );
    Serial.println( "%" );
  }
  server.handleClient();
  MDNS.update();
//  Serial.println("Exiting loop");
}

Not working code:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h> 
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <dht_nonblocking.h>
#define DHT_SENSOR_TYPE DHT_TYPE_11

ESP8266WiFiMulti wifiMulti; 
ESP8266WebServer server(80);

static const int DHT_SENSOR_PIN = 12;
DHT_nonblocking dht_sensor( DHT_SENSOR_PIN, DHT_SENSOR_TYPE );

static float global_temp;
static float global_humidity;
static unsigned long global_dht_measure_time = millis( );

static bool measure_environment( float *temperature, float *humidity )
{
  Serial.println( "Measuring DHT" );
  /* Measure once every 3 seconds. */
  if( millis( ) - global_dht_measure_time > 3000ul )
  {
    if( dht_sensor.measure( temperature, humidity ) == true )
    {
      global_dht_measure_time = millis( );
      return( true );
    }
    Serial.println( "DHT Failure" );
  }
  return( false );
}

String prepareJsonObject()
{
  if( measure_environment( &global_temp, &global_humidity ) == true )
  {
    Serial.print( "T = " );
    Serial.print( global_temp, 1 );
    Serial.print( " deg. C, H = " );
    Serial.print( global_humidity, 1 );
    Serial.println( "%" );
  }
  String res =
    String("{") +
    "\"Temperature\": " + String(global_temp) + "," +
    "\"Humidity\": " + String(global_humidity) + "," +
    "\"MeasureTime\": " + String(millis( ) - global_dht_measure_time) +
    "}";

  return res;
}

void handleRoot() {
  Serial.println("Handling Root");
  server.send(200, "application/json", prepareJsonObject());
}

void handleNotFound(){
  server.send(404, "text/plain", "404: The requested page was not found");
}

void setup(void){
  Serial.begin(115200);
  delay(10);
  Serial.println('\n');

  wifiMulti.addAP("ssid", "***");

  Serial.println("Connecting ...");
  int i = 0;
  while (wifiMulti.run() != WL_CONNECTED) {
    delay(250);
    Serial.print('.');
  }
  Serial.println('\n');
  Serial.print("Connected to ");
  Serial.println(WiFi.SSID()); 
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("AirSensor1")) {
    Serial.println("mDNS responder started");
  } else {
    Serial.println("Error setting up MDNS responder!");
  }

  server.on("/", HTTP_GET, handleRoot);
  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void){
  //Serial.println("Entering loop");

  server.handleClient();
  MDNS.update();
  //Serial.println("Exiting loop");
}

In both cases, the web server starts up fine, but the modified code returns zeros (the initial values) for temp and humidity. This is because the call to 'dht_sensor.measure' fails and returns false so the values are not updated.

All I did is change the location of the call to 'measure_environment' to the function that handles the root request, rather than the main loop. The expected effect being that the sensor only gets used when a client is asking for the data. For some reason this causes the sensor not to work.

My initial thought was that the sensor library requires frequent calls to measure but then that was refuted when I changed to working code measurement interval to 1 minute, and everything worked fine.

Yes, I can simply use the working code and forget about it but

  1. I am slightly allergic to global variables
  2. I just want to know what I have done to upset the DHT11!
\$\endgroup\$
8
  • \$\begingroup\$ In the second case, your measure_environment() has more debug prints, have you tried getting rid of them? The one in handleRoot() too? Otherwise the only DHT11-specific quirk I know is that it requires some time between queries, but you seem to be handling that properly. \$\endgroup\$
    – anrieff
    Commented Jan 6, 2019 at 16:59
  • 1
    \$\begingroup\$ If you suspect it is a HW behaviour issue, I'm afraid the only way to check that would be to probe DHT's signal pin with a logic analyzer. \$\endgroup\$
    – anrieff
    Commented Jan 6, 2019 at 17:03
  • 1
    \$\begingroup\$ @jsotola, this is becoming a programming thread, but the static keyword for functions has completely different semantics than static variables, and doesn't do any harm in OP's case. \$\endgroup\$
    – anrieff
    Commented Jan 7, 2019 at 4:58
  • 3
    \$\begingroup\$ Questions about a problem that could be either in firmware or hardware are perfectly on-topic here, as are questions about writing firmware for microcontrollers. So you don't need to worry about this being off-topic. An off-topic programming question would rather be something generic and not hardware-related, like "how to implement a binary search tree". \$\endgroup\$
    – Lundin
    Commented Jan 7, 2019 at 12:25
  • 1
    \$\begingroup\$ Also this appear to be some Arduino-flavoured C++, not C. You should change the tag. \$\endgroup\$
    – Lundin
    Commented Jan 7, 2019 at 12:33

2 Answers 2

1
\$\begingroup\$

I'll have a (very belated) go:

The measure_environment() function has a timer in it, so it will not return true and set values on every call, but only for one call out of all calls during three seconds.

Having it at the top of the loop() guarantees it will do its job every three seconds, and after the first three seconds the variables will always be set to something.

In the version where you call it from handleClient(), handleRoot(), prepareJsonObject(), there is an if in prepareJsonObject() that runs measure_environment(), and because of the timer in measure_environment() it could well return false there, because the timer isn't ready yet.

I suspect it is not dht_sensor.measure() that returns false, but that measure_environment() does.

Anyway, I don't think it is a good idea to have the timer in the function itself, as the calling code can't tell what the reason is for the function's returning true or false: the sensor not working, or the timer not being ready.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ I think you have probably solved it but I no longer have this hardware to test. But what you say makes sense to me. \$\endgroup\$ Commented Dec 19, 2020 at 12:44
  • \$\begingroup\$ @Chechy-Levas: Well, good to see you are still looking at it; hadn't expected an accept on an answer to a 2-year-old question. \$\endgroup\$
    – ocrdu
    Commented Dec 19, 2020 at 12:47
  • 1
    \$\begingroup\$ @ocrdu: I've upvoted your answer. Maybe you'll get a necromancer badge. \$\endgroup\$
    – JRE
    Commented Dec 19, 2020 at 12:59
  • \$\begingroup\$ @JRE: Thanks; I hope others are of like mind. \$\endgroup\$
    – ocrdu
    Commented Dec 19, 2020 at 13:02
  • 1
    \$\begingroup\$ It's a good, correct answer. That's why it got my vote. \$\endgroup\$
    – JRE
    Commented Dec 19, 2020 at 13:08
0
\$\begingroup\$

In the working code, you call measure_environment repeatedly, which that function seems to want. In the broken code, you only call it once, at setup, through some callback.

\$\endgroup\$
1
  • \$\begingroup\$ In both codes, the function 'handleRoot' is hooked to the event that a client requests the root page (the only page I am providing). In the non working code, the 'handleRoot' function also calls measure_environment. So every time a client connects, it calls the measure_environment function. The issue is that within meaure_environment, the call to 'dht_sensor.measure' returns false (fails). \$\endgroup\$ Commented Jan 7, 2019 at 16:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.