Blocking

I have been using Arduino for about 2 years now and it seems like a problem I almost always run into with these things is that eventually they start doing random things for unexplainable reason. Things like ignoring lines of code, not responding to user interaction, freezing data readouts, displaying incorrect sensor data, etc.

The problem I currently have is with all each of those issues… But what’s confusing me, is that while I have made VERY few changes to this code over a period of only a couple months, the problems are getting progressively worse over time.

My current project uses a Sainsmart Arduino Nano, a uLCD32-PTu from 4D systems, 2 Dallas DS18B20 temperature sensors and a 2 channel relay. All my controller is meant to do is essentially work as a thermostat. If one of the sensor’s heat reading is higher than the corresponding Slider value, turn the corresponding relay on and then off when temperature is lower. Simple enough?

I will focus on one problem at a time in order of significance…
Problem: At completely random times during operation, temperature data that is being sent to display on my LCD will simply freeze it’s last read data on the screen…but after a time span of 3 minutes, 5 minutes, or even 15 minutes, it will randomly start reading the live temperature data again.

The other half of my problem, is that if I use the LCD to change either of my slider values, there is sometimes a VERY long delay in response to the value change, or sometimes no response at all when it should be, and used to be immediate. Further confusing me, if I change the value of a slider, it sometimes seems to control the wrong corresponding relay.

Clearly, my Arduino is getting VERY confused somewhere and I have no idea why it’s blocking and having other issues like this… I may just need a set of fresh eyes to proof read my code and make sure I’m doing data calls correctly. Has anyone had a problem like this and if so, how did you fix it?

Here is my code

#define CHANNEL1 8 //LowSpeed RELAY
#define CHANNEL2 9 //HighSpeed RELAY

#include <genieArduino.h>

#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2 //DALLAS serial sensors go into input 3
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);
DeviceAddress coolant = { 0x28, 0x1F, 0x70, 0x66, 0x04, 0x00, 0x00, 0x0B };
DeviceAddress cooly = { 0x28, 0xB3, 0x58, 0x66, 0x04, 0x00, 0x00, 0xEC };

int sliderVal = 170;
int sliderVal2 = 190;

int slider0_val = 190;//Set point for high speed fan
int slider1_val = 170;//Set point for low speed fan 
int humidity, t, temp;



void setup()
{
  
  pinMode(CHANNEL1, OUTPUT); //Low Speed Fan RELAY
  pinMode(CHANNEL2, OUTPUT); //High Speed Fan RELAY
  
  sensors.begin(); // DALLAS sensors begin reading
  sensors.setResolution(coolant, 10);
  sensors.setResolution(cooly, 10);
  
  genieBegin (GENIE_SERIAL, 56000);  //Serial0 for 4D Display
  
  genieAttachEventHandler(myGenieEventHandler);

  //Reset the Display (change D4 to D2 if you have original 4D Arduino Adaptor)
  pinMode(4, OUTPUT);  // Set D4 on Arduino to Output (4D Arduino Adaptor V2 - Display Reset)
  digitalWrite(4, 1);  // Reset the Display via D4
  delay(100);
  digitalWrite(4, 0);  // unReset the Display via D4

  delay (3500); //let the display start up

    //Preset values upon device turn on
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 6, sliderVal2);
    genieWriteObject(GENIE_OBJ_SLIDER, 0, sliderVal2);
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 8, sliderVal);
    genieWriteObject(GENIE_OBJ_SLIDER, 1, sliderVal);
  
}



void loop()
{
  static long waitPeriod = millis();

  genieDoEvents(); // Do this every loop, checks the input buffer for data from the display and runs myGenieEventHandler()
  sensors.requestTemperatures(); 
  //Read from sensor every 100ms and write to the display
  //delay(1000);
  if (millis() >= waitPeriod)
  {
    temp = (t * 1.8) + 32; //Convert *C to *F.
    //sensors.requestTemperatures(); 
    
    float inside = sensors.getTempF(coolant);
    float outside = sensors.getTempF(cooly);
    
    //float tempC = sensors.getTempC(insideThermometer);
    //insideThermometer = sensors.requestTemperatures(); // Send the command to get DALLAS temperatures
    float tempF;
    sensors.requestTemperatures();    
    
    Serial.println (inside);////////GOOOD CODE 
    Serial.println (outside);///////GOOD CODE AGAIN... YAY!
    
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 0, inside);//The first line of code in the section gets ignored for some reason...?? copy and paste
    genieWriteObject(GENIE_OBJ_ANGULAR_METER, 0, inside);//HOME SCREEN TEMP GAUGE.........FORM 0 
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 0, inside);//HOME SCREEN TEMP..................FORM 0
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 1, outside);//LOW SPEED FAN CONTROL.............FORM 1
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 7, outside);//HIGH SPEED FAN CONTROL............FORM 5
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 9, outside); //HOME SCREEN OUTPUT SENSOR........FORM 0 
    genieWriteObject(GENIE_OBJ_ANGULAR_METER, 1, outside);//HOME SCREEN COOLANT OUTPUT GAUGE.......FORM 0 
    
    //waitPeriod = millis() + 1000; //originally set to 10, now set to 1000 for testing purposes update:value change seemed to make it worse
 
 
 ///////////NEEDS TO CHANGE SENSORS//////////////////////
  if (slider0_val > outside)//FAN SLIDER SET POINT WITH DALLAS SENSOR...For some reason it's  responding in polar opposites of the command? 
  {
    digitalWrite(CHANNEL1, HIGH); // FAN
  }
  else
  {
    digitalWrite(CHANNEL1, LOW); // FAN
  }
 /////////////////////////////////////////////////////
  
  if (slider1_val > outside)
  {
    digitalWrite(CHANNEL2, HIGH);
  }
  else
  {
    digitalWrite(CHANNEL2, LOW);
  }
 }
 
}

void myGenieEventHandler(void)
{
  genieFrame Event;
  genieDequeueEvent(&Event);

  //If the cmd received is from a Reported Event
  if (Event.reportObject.cmd == GENIE_REPORT_EVENT)
  {
    if (Event.reportObject.object == GENIE_OBJ_SLIDER)            // If the Reported Message was from a Slider
    {
      if (Event.reportObject.index == 0)                          // If Slider0
      {
        slider0_val = genieGetEventData(&Event);                  // Receive the event data from the Slider0
        Serial.println (slider0_val);////////GOOOD CODE WE'LL SEE
       
      }
    }
  }

 //If the cmd received is from a Reported Event
  if (Event.reportObject.cmd == GENIE_REPORT_EVENT)
  {
    if (Event.reportObject.object == GENIE_OBJ_SLIDER)            // If the Reported Message was from a Slider
    {
      if (Event.reportObject.index == 1)                          // If Slider1
      {
        slider1_val = genieGetEventData(&Event);                  // Receive the event data from the Slider1
        Serial.println (slider1_val);////////GOOOD CODE WE'LL SEE
      }
    }
  }
////////////////////////////////////////////////
  //If the cmd received is from a Reported Object, which occurs if a Read Object is requested in the main code, reply processed here.
  if (Event.reportObject.cmd == GENIE_REPORT_OBJ)
  {
    
    //Put code in here, like the above, if you want to process data when you have used a genieReadObject in your main loop
    //Currently there are none, so this section has no code.
    //Filter out the Object and the Index like above, and then same the data into a variable as required.
  }
}

Gustermaximus:

  static long waitPeriod = millis();

//delay(1000);
  if (millis() >= waitPeriod)
  {

This will not handle millis() wrapping -- although this happens on a 49-day timescale and is not what you are reporting. You should follow the approach set out in the blink-without-delay sketch to do this timing. It essential to subtract the base/start time from the current time and compare to the elapsed time criteria in order to properly handle millis() overflow.

gardner:

Gustermaximus:

  static long waitPeriod = millis();

//delay(1000);
 if (millis() >= waitPeriod)
 {

This will not handle millis() wrapping -- although this happens on a 49-day timescale and is not what you are reporting. You should follow the approach set out in the blink-without-delay sketch to do this timing. It essential to subtract the base/start time from the current time and compare to the elapsed time criteria in order to properly handle millis() overflow.

equally important to use UNSIGNED long integer type in a millis() timer.:

if (millis() - lastTimeRan >= myInterval)
{
  // do something
  lastTimeRan = millis();
}

where lastTimeRan and myInterval are UL's

I have never fully understood how using millis in the blink without delay works, I only have it in my code because I have copy and pasted a lot from the 4D system's example codes. @BulldogLowell Do I simply copy and paste your code:

if (millis() - lastTimeRan >= myInterval)
{
  // do something
  lastTimeRan = millis();
}

and replace my current millis command brackets with that? I apologize if that's a stupid question, but I only learn how these codes work when I seen them completed and being used correctly. Do I need to add ints for "lastTimeRan" or "myInterval" in order for this to work? I'm not asking you to write my code for me, but due to my lack of experience with this, I'm not sure if what you posted was a complete code or not?

Update: I changed my code to...

 unsigned long waitPeriod = millis();//Changed from static to unsigned 10/28/14
  //unsigned long lastTimeRan = waitPeriod
 
  genieDoEvents(); // Do this every loop, checks the input buffer for data from the display and runs myGenieEventHandler()
  sensors.requestTemperatures(); 
  //Read from sensor every 100ms and write to the display
  //delay(1000);
  if (millis() >= waitPeriod)
  {
    temp = (t * 1.8) + 32; //Convert *C to *F.
    //sensors.requestTemperatures(); 
    
    float inside = sensors.getTempF(coolant);
    float outside = sensors.getTempF(cooly);
    
    //float tempC = sensors.getTempC(insideThermometer);
    //insideThermometer = sensors.requestTemperatures(); // Send the command to get DALLAS temperatures
    float tempF;
    sensors.requestTemperatures();    
    
    Serial.println (inside);////////GOOOD CODE 
    Serial.println (outside);///////GOOD CODE AGAIN... YAY!
    
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 0, inside);//The first line of code in the section gets ignored for some reason...?? copy and paste
    genieWriteObject(GENIE_OBJ_ANGULAR_METER, 0, inside);//HOME SCREEN TEMP GAUGE.........FORM 0 
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 0, inside);//HOME SCREEN TEMP..................FORM 0
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 1, outside);//LOW SPEED FAN CONTROL.............FORM 1
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 7, outside);//HIGH SPEED FAN CONTROL............FORM 5
    genieWriteObject(GENIE_OBJ_LED_DIGITS, 9, outside); //HOME SCREEN OUTPUT SENSOR........FORM 0 
    genieWriteObject(GENIE_OBJ_ANGULAR_METER, 1, outside);//HOME SCREEN OUTPUT GAUGE.......FORM 0 
    //genieWriteObject(GENIE_OBJ_LED_DIGITS, 6, sliderVal);//TEST
    //genieReadObject(GENIE_OBJ_SLIDER, 0);//TEST
    
    
    //waitPeriod = millis() + 100; //originally set to 10, now set to 1000 for testing purposes
 
 
 ///////////NEEDS TO CHANGE SENSORS//////////////////////
  if (slider0_val > outside)//RADIATOR FAN SLIDER SET POINT WITH DALLAS SENSOR...For some reason it's  responding in polar opposites of the command? 
  {
    digitalWrite(CHANNEL1, HIGH); //FAN
  }
  else
  {
    digitalWrite(CHANNEL1, LOW); //FAN
  }
 /////////////////////////////////////////////////////
  
  if (slider1_val > outside)
  {
    digitalWrite(CHANNEL2, HIGH);
  }
  else
  {
    digitalWrite(CHANNEL2, LOW);
  }
 }
 
waitPeriod = millis() + 10;
}

Sensors seem to be working quite well without any hick-ups, but my relays are almost completely unresponsive to slider value changes coming from the LCD. Any thoughts on why only the relay if commands are being blocked somehow now?

if (millis() >= waitPeriod)
  {
     ...
   }
 
 waitPeriod = millis() + 10;

Will wait forever because it is set +10 on every loop.

You also should modify your code as previously suggested following the example...

if (millis() - lastTimeRan >= myInterval)
{
  // do something
  lastTimeRan = millis();
}

if (millis() >= waitPeriod)millis() starts at zero when you power up or restart your arduino and continues counting ad infinitum, like a clock. That line of code will only be false for the number of milliseconds in one waitPeriod. Once it becomes true, it will remain true for around 50 days, unless you do a restart.
What you need is something like this:

if (millis()- startTime >= waitPeriod){
//do your stuff here
startTime =millis();  //reset startTime for the next waitPeriod
}

I compare this to boiling an egg.
Put an egg into boiling water and make a note of the start time.
Keep looking at the clock [millis()] to see if the current time is 3 minutes greater than the start time.
When the current time [milis()] - start time >= 3 minutes, take the egg out of the water.
To boil another egg in the same water, you will need a new start time.

Another issue is your use of:

float inside = sensors.getTempF(coolant);
float outside = sensors.getTempF(cooly);

If, sensors.setWaitForConversion(true);
Then conversion will block for 500ms or more for each conversion.
Therefore waitPeriod is meaningless in the current loop coding.

If, sensors.setWaitForConversion(false);
Then you must do something else, like update you display, read sliders for a 1000ms before reading the resulting temperature.

I suggest adding the timer library and use that to start conversions, wait for results and update a variable used for display. A second timer event could be used to update the display and avoid waitPeriod altogether.

@Henry_Best I added you suggested code:

if (millis()- startTime >= waitPeriod){
//do your stuff here
startTime =millis();  //reset startTime for the next waitPeriod
}

but I got an error message saying " ‘startTime’ was not declared in this scope "
Do I need to make an int or a float for it? Or am I missing a library attatchment up top?

@George Matthews

If, sensors.setWaitForConversion(true);
Then conversion will block for 500ms or more for each conversion.
Therefore waitPeriod is meaningless in the current loop coding.

If, sensors.setWaitForConversion(false);
Then you must do something else, like update you display, read sliders for a 1000ms before reading the resulting temperature.

I have never seen a command like that before, can you explain what "setWaitForConversion" is?

sensors.setWaitForConversion is a function within DallasTemperature library.
I thought I did explain.

but I got an error message saying " 'startTime' was not declared in this scope "
Do I need to make an int or a float for it?

The easiest thing would be to declare it as a global unsigned long at the start of the program.

unsigned long because that allows the millis() rollover to work as discussed in this thread
global because it makes the value available to the whole program

Could be a power (loading) problem.

Sainsmart Arduino Nano           mA 
Dallas DS18B20 temp sensors      mA
2 channel relay                  mA
uLCD32-PTu display           215 mA max
TOTAL                        ___ mA

Could you post your connection details or preferably a schematic?

@UKHeliBob I added" unsigned long startTime(); " to the beginning of my program and got this error:
" invalid operands of types 'long unsigned int' and 'long unsigned int () ()' to binary 'operator-' "
I'm not sure how to solve this one..

Should have been:

unsigned long startTime;

@dlloyd I am working on putting together a schematic, however, I have had a discussion on one of these forums about how I am powering everything through a 5 volt regulator and powering the screen and the relays directly from the regulator and only using the 5v and GND pins on the Nano to power the sensors. I ran everything through a meter and all numbers seem to be in fairly healthy ranges, however, I have not ruled out power supply just yet.

I have had a very similar problem like this using an Arduino Uno and a sainsmart 16x2 lcd shield with a very similar sensor and relay setup, but powered directly from a wall power supply and through USB with strange blocking results. Buttons on the LCD shield would sometimes not respond at all, take several presses or need long hold presses in order for the assigned action to happen..

I have had several encounters with blocking that I cannot explain even with the simplest of codes and I am trying to figure out why this happens and how to fix it

@wildbillfacepalm I probably should've seen that... lol compiled properly and now I'm going to test it

Here is a video of the device in operation so you guys can see what's actually goin on: Adriano Nano, uLCD32-PTu, DS18B20 sensors blocking - YouTube

Those relays, with both coils energized will require about 200mA from the regulator. Just the display and relay board will add up to 400+ mA demand on the Arduino regulator.

It’s really advisable to use a separate power source for the relay board. That way you could resolve any overloading problems and get full opto-isolation from the relays.

… easy to do as a test to see if it resolves the problem.