Using Interrupts

Hi all,

I’m new to the Arduino (and reasonably new to “proper” programming as well!) and I’ve run into an issue with attachInterrupt which I’m sure is purely down to my lack of knowledge!

I have a Dallas DS18S20 and I am trying to put together a device which performs an action based on when the temperature changes.

The issue I have run into is that I need to have constant polling of other devices occurring at the same time, so I thought that attachInterrupt would work

The DS18S20 is wired in the usual way (4.7K pull-up resistor, pin 2 on sensor → pin2 on arduino (or seeeduino in my case)) and my current code is as follows:

#include <OneWire.h>
OneWire ds(2);

void setup(void){
 Serial.begin(9600);
 attachInterrupt(0,readTemp, CHANGE); 
}

void readTemp(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;

  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return;
  }

  // the first ROM byte indicates which chip
  switch (addr[0]) {
  case 0x10:
    //      Serial.println("  Chip = DS18S20");  // or old DS1820
    type_s = 1;
    break;
  case 0x28:
    //    Serial.println("  Chip = DS18B20");
    type_s = 0;
    break;
  case 0x22:
    //  Serial.println("  Chip = DS1822");
    type_s = 0;
    break;
  default:
    //Serial.println("Device is not a DS18x20 family device.");
    return;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end

  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.

  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
  }
  // convert the data to actual temperature

  unsigned int raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // count remain gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } 
  else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw << 3;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw << 2; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw << 1; // 11 bit res, 375 ms
    // default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  Serial.println(celsius);
}
void loop(void){
}

As you can see, this is basically the “dallas temperature” example that comes with the OneWire library, however I think I’m “doing it wrong” when it comes to configuring the interrupts.

Any help that can be provided (especially links to further reading etc.) are more than welcome.

Kind regards,

Prof

The attachInterrupt() function registers an event handler to be invoked when an event occurs. You need some event to occur to cause the event handler to be called.

If the interrupt handler DOES get invoked, it need to be fast. Very fast, so that it does not cause other interrupts to be ignored while your handler is dinking around taking it's time doing something.

    delay(250);

That's hardly fast, is it. The delay() function relies on interrupts (the clock ticking generates an interrupt). Interrupts are disabled during your function. So, you absolutely can not use delay() in an ISR. And don't try to get around this with some other time wasting technique.

    Serial.println("CRC is not valid!");

This isn't fast, either.

  delay(1000);     // maybe 750ms is enough, maybe not

You got the part about fast, right?

  Serial.println(celsius);

Not in an ISR...

Is the DS18S20 even generating an interrupt?

DS18S20 and all other OneWire bus devices cannot interrupt, its a strictly polling only bus.

If you do manage to attach an interrupt to the one-wire bus you'll get vast swaves of interrupts everytime you talk to the bus.

You need to regularly poll the DS18S20. You don't want to call delay(). So what you do is every second or so read the temperature from the device and set another conversion going. By the time the next second comes along the next conversion is guaranteed ready.

For doing some action on a regular basis with using delay please look at the BlinkWithoutDelay() example sketch...

Here is some code I made up. I also made it into a library :slight_smile:

//execute function g() every interval ms
void timer(unsigned long interval, void (*g)()){
  static unsigned long prev = 0;
  if (millis() - prev >= interval){
    g();
    prev = millis();
  }
}
//execute function g() every interval ms, times times only
void timer(unsigned long interval, void (*g)(), unsigned long times){
  static unsigned long prev = 0;
  static unsigned long counter = 0;
  if (millis() - prev >= interval && counter < times){
    g();
    prev = millis();
    counter++;
  }
}
//function to be run
void blinking(){
  static boolean state = 1;
  digitalWrite(13, state);
  state = !state;
}
void fading(){
  static byte brightness = 0;
  static int fadeAmount = 1;
  analogWrite(9, brightness);    
  brightness = brightness + fadeAmount; 
  if (brightness == 0 || brightness == 255) {
    fadeAmount = -fadeAmount ; 
  }      
}
void setup(){
  pinMode(13, OUTPUT);
}
void loop(){
  //blink the LED on pin 13 every 500 ms 5 times only
  timer(500, blinking, 5);
  //fade an LED on pin 9 forever...
  timer(30, fading);
}

This will not halt your program in any way. Just don’t use delay if you use my code! This code demonstrates multitasking without delay. It uses millis().

Also look at the Multi_Blink example in the Playground. You can adapt this to your purposes by putting calling appropriate functions to do what you need rather than just blinking LEDs.