Strange problems with negative values and signed long type

Hi,
I am experiencing some weird behaviour when certain values get negative. I've reviewed the code many times but I can't find where I'm going wrong.

The project is a temperature and humidity sensor based on the DHT22 sensor (it also still supports the DHT11). It also calculates the dew point from these 2 values. It all works well when all values are positive. Now in winter I have negative values for the first time and the sketch falls over...

The values are requested via serial port using my own "protocol" which consists of two bytes with value 0xCC (indicating the start of a command) then 16bit deviceID, 16bit valueID, 8bit actionID (get, set, reply, event), 8bit statusID (to report back any error as a number) and then two 32bit signed integers containing the actual values (value1 and value2) (most of the time value2 is zero when only one value is needed).

Some of the relevant parts of the code are:

some global variables and defines:

// DHT11/22 sensor  variables and pins
#define pin_DHT 12       // DHT11/22 data pin
#define DHTtype 22       // 11 = DHT11, 22 = DHT22
long temp, hum, dew;     // current values for temperature, dewpoint and humidity in 1/10th degrees Celsius / 1/10th percent
unsigned long DHTmillis; // timestamp of last sensor read
dht DHT;                 // DHT sensor object
//Serial comms variables
unsigned int deviceID = 0;
unsigned int valueID = 0;
byte actionID = 0;
byte statusID = 0;
long value1, value2 = 0;

in setup() the temperature and humidity are being read for the first time. Then in loop() it is being updated every 10 seconds (exact same code)

  if (DEVICE_TYPE & DEVICE_TYPE_TEMPHUM) // read DHT sensor if this is a TEMPHUM device
  {
    // check if millis() has rolled over
    if (millis() < DHTmillis) DHTmillis = millis();   

    //read DHT sensor every 10 seconds
    if (millis() > (DHTmillis + 10000))
    {
      if ((DHTtype == 11) || (DHTtype == 22))
      {
        if (DHTtype == 11) DHT.read11(pin_DHT);
        if (DHTtype == 22) DHT.read22(pin_DHT);
        temp = (DHT.temperature * (double)10);
        hum  = (DHT.humidity * (double)10);
        dew  = (dewPoint(DHT.temperature, DHT.humidity) * (double)10);
      }
      else
      {
        temp = -1L;
        hum  = -1L;
        dew  = -1L;
      }
      DHTmillis = millis(); // remember when sensor was read
    }
  }

Then, when a command comes in to read the temperature, the following function is being called (very similar functions exist for humidity and dew point)

void f_temperature()
{
  if (DEVICE_TYPE & DEVICE_TYPE_TEMPHUM)
  {
    if (actionID == Action_Set)
    {
      statusID= 106; // Action_Set not supported for temperature
    }

    else if (actionID == Action_Get)
    {
      statusID= 0;
      value1 = temp; 
      value2 = 0;
    }
    actionID = Action_Reply;
    serial_send();
  }
}

serial_send() looks like this:

void serial_send()
{
  byte data[16];
  data[0] = 0xCC;
  data[1] = 0xCC;
  data[2] = id >> 8;
  data[3] = (id & 0xFF);
  data[4] = valueID >> 8;
  data[5] = (valueID & 0xFF);
  data[6] = actionID;
  data[7] = statusID;
  data[8] = value1 >> 24;
  data[9]  = (value1 & 0x00FF0000L) >> 16;
  data[10] = (value1 & 0x0000FF00L) >> 8;
  data[11] = (value1 & 0x000000FFL);
  data[12] = value2 >> 24;
  data[13] = (value2 & 0x00FF0000L) >> 16;
  data[14] = (value2 & 0x0000FF00L) >> 8;
  data[15] = (value2 & 0x000000FFL);
  Serial.write(data, 16);
}

Now, when the value is positive it all works fine and I get a data packet back on the serial port. However, if a value gets negative I get no reply at all. It just does not reply! It does still reply for other values that are positive, so it's not totally unresponsive.

I would be grateful for any hints as to where and why this is going wrong and what to do about it.

I think that "byte" is "unsigned char". I don't know if it have something to do about it or not.

// check if millis() has rolled over
if (millis() < DHTmillis) DHTmillis = millis();

//read DHT sensor every 10 seconds
if (millis() > (DHTmillis + 10000))

by using subtraction the rollover is automatically solved in oneline

//read DHT sensor every 10 seconds
if ((millis() - DHTmillis) > 10000))
{
DHTmillis += 10000;
....

Can you post the whole sketch in one block? that is far more convenient debugging, at least for me.

I'll have a look at these millis... thanks for that :wink:
But I don't think it relates to the problem. :wink:

I can certainly post the whole sketch. I copied the important (I think) parts out because the sketch does a lot more than what I explained. It also controls 8 bistable relays which switch lights on and off, 4 of which are dimmable and it reads a door and a window lock contact and reports back if they are open or closed. This is all controlled from my home automation project. There are many of these devices round my house but the others don't have quite as many functionality. This one is currently the only one exposed to low temperatures as it is located in my observatory (i.e a shed).

So the sketch is a bit bigger than you might expect. I have attached it.

Obsy_Lights.ino (20.9 KB)

Oh, and it runs on a Arduino Mega2560. Most of the other devics are Minis328 with pretty much the same code for the temperature and humidity measurements.

I'd suggest that as soon as you detect you have sub zero conditions, you shouldn't be attempting to calculate a dew point, as any result you get is totally irrelevant.

Humidity (absolute, or relative) and therefore dewpoint, are totally invalid concepts below freezing point.

I actually thought of that. The first time I noticed it happening was when the dew point got negative. I checked that formula suspecting something like a division by zero. I also checked whether a dew point can go negative. I found that yes, it is (mathematically) possible but, as you say, it doesn't make much sense.

But then I realised that it also happens to temperature and humidity. If I set the values of temperature, humidity and dew point all to fixed -1 and without calculating the dew point (simply by setting the DHT type to something other than 11 or 22 which causes the sketch not to read the sensor and set all 3 values to -1L), the same problem happens.

Also, the dew point is not being calculated at the time the value is being requested but it is done in loop() every 10 seconds when the DHT sensor has been read.

I suspect something is going wrong with variable types, "long" in this case, probably when shifting bits in my serial_send() function.

From what I see as symptoms it looks to me as if something is causing the Arduino to skip some piece of code (possibly the equivalent of an exception, if that exists?) and start loop() from the beginning.

I'm still hoping someone will find an error in my coding somewhere. I might have to put some extra code in to check exactly what code it is and isn't executing.

OK, I found it and fixed it. It wasn't the Arduino at all :fearful:

It was my Windows software that communicates with the Arduino. It was interpreting the incoming data as 32bit UNSIGNED integer and conversion failed. That's why these packets got lost.

yesyes:
OK, I found it and fixed it. It wasn't the Arduino at all :fearful:

It was my Windows software that communicates with the Arduino. It was interpreting the incoming data as 32bit UNSIGNED integer and conversion failed. That's why these packets got lost.

Thanks for the update.

BTW good catch!