Read temperature using a Timer

Hi,

I have been playing with the latest firmware and the timer() trying to see if I can read the temperature from a ds18b20 every 15 seconds, whilst doing other things. If I use the temperature code in the main loop I get temperature readings e.g. 26C. However as soon as I move this to a function that is called every 15 seconds the celsius reading is -0.062500.

If I am going about this the wrong way can you suggest a better approach? As an example use case this is a starting point of what I want to achieve…

Take a temperature reading every 15 seconds and publish the value.
On an interrupt if motion is detected from a PIR sensor publish that motion has been detected and flash an LED with a delay of 1 second.
On a request take a light level reading from an LDR using Particle.function() to take a reading now and Particle.variable() to retrieve the value.

This is just a basic example but including a few techniques I want to be able to use together.

Some sample code is below any help would be much appreciated.

Many thanks

Andy


// This #include statement was automatically added by the Particle IDE.
#include "Particle-OneWire/Particle-OneWire.h"

// Globals
OneWire  ds(D2);

int LED = D7; // pin for LED
//int PIR = D3; //pin for PIR motion sensor

Timer tempTimer(13750, take_temperature);
Timer ledTimer(1000, blink_led);


void setup()
{
    Serial.begin(9600);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    //pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    tempTimer.start();
}

void blink_led()
{
    //blink light
    digitalWrite(LED, HIGH);
    delay(1000);
    digitalWrite(LED, LOW);
}


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

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

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

  // 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

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t 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);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 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;
  fahrenheit = celsius * 1.8 + 32.0;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Particle.publish("temperature/celsius/", String(celsius));
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
  Particle.publish("temperature/celsius/", String(celsius));
}

your blink_led() function is blocking... every second for a second

try this instead:

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

I suggest you use a lib for the DS18B20. I have one that does CRC checks and an example app that shows timed temp readings. It is in build and on GitHub under the same username.

To add to @BulldogLowell’s post, try to treat timer callbacks like ISRs - keep them short and fast.

For this avoid blocking, delays, calls to long running sub routines, …

You’ve got a lot of things going on in your routine, so I’d rather not directly do all that in the callback.
For your use case, I’d put the temp reading functionality in a dedicated function, set a flag inside the timer which then instructs loop() to call your function. I guess there is not a lot otherwise going on in your loop() and the timing is not as crucial (exact 15sec ;-))

Hi,

Thanks for the replies so, I’ve taken some of the advice and knocked together a little more of my hypothetical example (see code below).

A few question, the temp reading is blocking like you say, what I wondered… is there a way to have this run in the background almost like a mundane operation like the breathing cyan led?

In my code example I’ve tried to cover a few typical use case scenarios, so operations that occur at regular intervals, things that are fired on interrupts, stuff that is just going on in the main loop, publishes and variables that are requested at any time.

Any feedback or pointers would be much appreciated and going back to the other part of the original problem, the temperature value was reading -0.062500 when called like the code below, but returned 26 celsius when running in the main loop, any ideas?

Many thanks

Andy

// This #include statement was automatically added by the Particle IDE.
#include "Particle-OneWire/Particle-OneWire.h"

// Globals
OneWire  ds(D2);

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

double celsius = 0, fahrenheit = 0;

Timer tempTimer(13750, take_temperature);
Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Serial.begin(9600);
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    tempTimer.start();
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
    Particle.publish("motion/detected", "1");
}

void take_temperature()
{
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

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

  // 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

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t 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);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 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
  }
  convertToCelsius((double)raw);
  convertToFarenheit((double)raw);
}

void convertToCelsius(double value)
{
    celsius =  value / 16.0;
    Particle.publish("temperature/celsius/", String(celsius));
}

void convertToFarenheit(double value)
{
    fahrenheit =  (value / 16.0) * 1.8 + 32;
    Particle.publish("temperature/celsius/", String(celsius));
}

Hi @LukeUSMC

I followed your example however both my code and your example code fail to compile; with the error:

timetest1.cpp: In function 'void loop()':
timetest1.cpp:60:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
^
timetest1.cpp: In function 'void publishData()':
timetest1.cpp:87:37: warning: precision used with '%c' gnu_printf format [-Wformat=]
Alarm alarm;
^
timetest1.cpp:87:37: warning: format '%c' expects argument of type 'int', but argument 3 has type 'double' [-Wformat=]
timetest1.cpp:87:37: warning: precision used with '%c' gnu_printf format [-Wformat=]
timetest1.cpp:87:37: warning: format '%c' expects argument of type 'int', but argument 3 has type 'double' [-Wformat=]
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::pinModeFastOutput()': Particle-OneWire/Particle-OneWire.cpp:128: multiple definition of OneWire::OneWire(unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:4: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::pinModeFastOutput()': Particle-OneWire/Particle-OneWire.cpp:128: multiple definition of OneWire::OneWire(unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:4: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::pinModeFastOutput()': Particle-OneWire/Particle-OneWire.cpp:140: multiple definition of OneWire::reset()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:16: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::write_bit(unsigned char)': Particle-OneWire/Particle-OneWire.cpp:177: multiple definition of OneWire::write_bit(unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:53: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::read_bit()': Particle-OneWire/Particle-OneWire.cpp:210: multiple definition of OneWire::read_bit()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:86: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::write(unsigned char, unsigned char)': Particle-OneWire/Particle-OneWire.cpp:239: multiple definition of OneWire::write(unsigned char, unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:115: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::write_bytes(unsigned char const*, unsigned short, bool)': Particle-OneWire/Particle-OneWire.cpp:256: multiple definition of OneWire::write_bytes(unsigned char const*, unsigned short, bool)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:132: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::read()': Particle-OneWire/Particle-OneWire.cpp:273: multiple definition of OneWire::read()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:149: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::read_bytes(unsigned char*, unsigned short)': Particle-OneWire/Particle-OneWire.cpp:284: multiple definition of OneWire::read_bytes(unsigned char*, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:160: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::select(unsigned char const*)': Particle-OneWire/Particle-OneWire.cpp:292: multiple definition of OneWire::select(unsigned char const*)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:168: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::skip()': Particle-OneWire/Particle-OneWire.cpp:304: multiple definition of OneWire::skip()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:180: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::depower()': Particle-OneWire/Particle-OneWire.cpp:307: multiple definition of OneWire::depower()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:183: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::reset_search()': Particle-OneWire/Particle-OneWire.cpp:323: multiple definition of OneWire::reset_search()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:199: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::target_search(unsigned char)': Particle-OneWire/Particle-OneWire.cpp:339: multiple definition of OneWire::target_search(unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:215: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::search(unsigned char*)': Particle-OneWire/Particle-OneWire.cpp:365: multiple definition of OneWire::search(unsigned char*)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:241: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::crc8(unsigned char*, unsigned char)': Particle-OneWire/Particle-OneWire.cpp:497: multiple definition of OneWire::crc8(unsigned char*, unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:373: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::crc16(unsigned char const*, unsigned short, unsigned short)': Particle-OneWire/Particle-OneWire.cpp:518: multiple definition of OneWire::crc16(unsigned char const*, unsigned short, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:394: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::check_crc16(unsigned char const*, unsigned short, unsigned char const*, unsigned short)': Particle-OneWire/Particle-OneWire.cpp:512: multiple definition of OneWire::check_crc16(unsigned char const*, unsigned short, unsigned char const*, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:388: first defined here
collect2: error: ld returned 1 exit status
make: *** [32b71066dfb399eab69ef17a60fe7786434beddc7a148bf503d5d0910dfb.elf] Error 1
Error: Could not compile. Please review your code.

Here is my updated code below...

Many thanks

Andy

// This #include statement was automatically added by the Particle IDE.
#include "Particle-OneWire/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

char szInfo[64];
float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime){
        getTemp();
    }

    if (millis() > MetricnextPublishTime){
        Serial.println("Publishing now.");
        publishData();
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
    Particle.publish("photon/motion/detected", "1");
}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    sprintf(szInfo, "%2.2c", celsius);
    Particle.publish("photon/temperature/celsius", szInfo, PRIVATE);
    sprintf(szInfo, "%2.2f", fahrenheit);
    Particle.publish("photon/temperature/fahrenheit", szInfo, PRIVATE);
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

If you are using the DS18B20 library, you need to remove Particle-OneWire since both define the same functions, hence the hard to misunderstand

To use the library correctly, you should have these includes

#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

@LukeUSMC, for the Particle Build (Web IDE), you might like to correct these includes and also fix the build error about missing displayupdate(); (ds18b20_test.ino:30:17: error: 'displayupdate' was not declared in this scope).

Hi @ScruffR

I made the amends as advised but still get the same errors:

timetest1.cpp: In function 'void loop()':
timetest1.cpp:61:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
     Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
                    ^
timetest1.cpp: In function 'void publishData()':
timetest1.cpp:88:37: warning: precision used with '%c' gnu_printf format [-Wformat=]
     Alarm alarm;
                                     ^
timetest1.cpp:88:37: warning: format '%c' expects argument of type 'int', but argument 3 has type 'double' [-Wformat=]
timetest1.cpp:88:37: warning: precision used with '%c' gnu_printf format [-Wformat=]
timetest1.cpp:88:37: warning: format '%c' expects argument of type 'int', but argument 3 has type 'double' [-Wformat=]
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::pinModeFastOutput()':
Particle-OneWire/Particle-OneWire.cpp:128: multiple definition of `OneWire::OneWire(unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:4: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::pinModeFastOutput()':
Particle-OneWire/Particle-OneWire.cpp:128: multiple definition of `OneWire::OneWire(unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:4: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::pinModeFastOutput()':
Particle-OneWire/Particle-OneWire.cpp:140: multiple definition of `OneWire::reset()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:16: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::write_bit(unsigned char)':
Particle-OneWire/Particle-OneWire.cpp:177: multiple definition of `OneWire::write_bit(unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:53: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::read_bit()':
Particle-OneWire/Particle-OneWire.cpp:210: multiple definition of `OneWire::read_bit()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:86: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::write(unsigned char, unsigned char)':
Particle-OneWire/Particle-OneWire.cpp:239: multiple definition of `OneWire::write(unsigned char, unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:115: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::write_bytes(unsigned char const*, unsigned short, bool)':
Particle-OneWire/Particle-OneWire.cpp:256: multiple definition of `OneWire::write_bytes(unsigned char const*, unsigned short, bool)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:132: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::read()':
Particle-OneWire/Particle-OneWire.cpp:273: multiple definition of `OneWire::read()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:149: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::read_bytes(unsigned char*, unsigned short)':
Particle-OneWire/Particle-OneWire.cpp:284: multiple definition of `OneWire::read_bytes(unsigned char*, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:160: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::select(unsigned char const*)':
Particle-OneWire/Particle-OneWire.cpp:292: multiple definition of `OneWire::select(unsigned char const*)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:168: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::skip()':
Particle-OneWire/Particle-OneWire.cpp:304: multiple definition of `OneWire::skip()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:180: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::depower()':
Particle-OneWire/Particle-OneWire.cpp:307: multiple definition of `OneWire::depower()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:183: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::reset_search()':
Particle-OneWire/Particle-OneWire.cpp:323: multiple definition of `OneWire::reset_search()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:199: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::target_search(unsigned char)':
Particle-OneWire/Particle-OneWire.cpp:339: multiple definition of `OneWire::target_search(unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:215: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::search(unsigned char*)':
Particle-OneWire/Particle-OneWire.cpp:365: multiple definition of `OneWire::search(unsigned char*)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:241: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::crc8(unsigned char*, unsigned char)':
Particle-OneWire/Particle-OneWire.cpp:497: multiple definition of `OneWire::crc8(unsigned char*, unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:373: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::crc16(unsigned char const*, unsigned short, unsigned short)':
Particle-OneWire/Particle-OneWire.cpp:518: multiple definition of `OneWire::crc16(unsigned char const*, unsigned short, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:394: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::check_crc16(unsigned char const*, unsigned short, unsigned char const*, unsigned short)':
Particle-OneWire/Particle-OneWire.cpp:512: multiple definition of `OneWire::check_crc16(unsigned char const*, unsigned short, unsigned char const*, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:388: first defined here
collect2: error: ld returned 1 exit status
make: *** [d2c72077268d5875b0e39b41a3c3a9caa52b0d9c85d89ffdf319f7968b3c.elf] Error 1

This is my code:

// This #include statement was automatically added by the Particle IDE.
//#include "Particle-OneWire/Particle-OneWire.h"
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

char szInfo[64];
float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime){
        getTemp();
    }

    if (millis() > MetricnextPublishTime){
        Serial.println("Publishing now.");
        publishData();
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
    Particle.publish("photon/motion/detected", "1");
}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    sprintf(szInfo, "%2.2c", celsius);
    Particle.publish("photon/temperature/celsius", szInfo, PRIVATE);
    sprintf(szInfo, "%2.2f", fahrenheit);
    Particle.publish("photon/temperature/fahrenheit", szInfo, PRIVATE);
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

Many thanks

Andy

Have you actually removed the Particle-OneWire library from your project by clicking the black circle (X) next to the library name?

Hi @ScruffR

My bad still getting used to the web IDE, its all compiling now thanks.

Andy

1 Like

Hi,

So I have my code deployed, the led flashes constantly as expected. I also get temperatures in both C and F logged out to my dashboard. However as soon as the PIR motion sensor is triggered, the code clearly stops and the led stays lit solid.

There is obviously an issue with the interrupt but I’m not sure why?

Many thanks

Andy

// This #include statement was automatically added by the Particle IDE.
//#include "Particle-OneWire/Particle-OneWire.h"
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime){
        getTemp();
    }

    if (millis() > MetricnextPublishTime){
        Serial.println("Publishing now.");
        publishData();
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
    Particle.publish("photon/motion/detected", "1");
}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    Particle.publish("photon/temperature/celsius", String(celsius));
    Particle.publish("photon/temperature/fahrenheit", String(fahrenheit));
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

Don’t publish inside an ISR and I wouldn’t create an automatic object inside and ISR either.

Rather set a flag in your ISR and act upon it in your loop().

Shoot. I thought I had already corrected that. I’ll get it fixed. Thanks.

1 Like

The example app has been updated and corrected. Version 0.1.02 is published and verified without warning. Sorry about that…supposed to be helping not confusing folks!

DS18B20 0.1.02
1 Like

Hi

Thanks for the help, I’ve tried a similar idea to the getTemp() check in the main loop, however I still observe the same behaviour in terms of the code freezing. I guess I’m still missing something?

Also out of interest are there any other techniques that I could employ or is the typical method to set flags and pick them up in the main loop?

Many thanks

Andy

// This #include statement was automatically added by the Particle IDE.
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime){
        getTemp();
    }

    if (millis() > MetricnextPublishTime){
        Serial.println("Publishing now.");
        publishData();
    }
    
    Alarm alarm;
    if (alarm.status == 1)
    {
        raiseAlarm();
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
}

void resetAlarm()
{
    Alarm alarm;
    alarm.status = 0;
}

void raiseAlarm()
{
    Particle.publish("photon/motion/detected", "1");
    resetAlarm();
}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    Particle.publish("photon/temperature/celsius", String(celsius));
    Particle.publish("photon/temperature/fahrenheit", String(fahrenheit));
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

you create an instance of Alarm class
you then assign "1" to Alarm::status
then it all gets destroyed when the ISR goes out of scope....

It isn't really clear (to me) what you are trying to do with Alarm class...

have you considered a slightly simpler approach like this (uncompiled, untested):

// This #include statement was automatically added by the Particle IDE.
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

float pubTemp;
double celsius;
double fahrenheit;
const int Metric_Publish_Rate = 30000;
const int DS18B20_SAMPLE_INTERVAL = 2500;
unsigned long metricPublishTime = 0;
unsigned long tempSampleTime = 0;

volatile bool isrFlag = false;

int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

// class Alarm {
//   public:
//     Alarm() {
//         Particle.function("alarm", &Alarm::alarm, this);
//     }
//     
//     int status;
// 
//     int alarm(String command) {
//         if(command == "reset"){
//             status = 0;
//         }
//         if(command == "panic"){
//             status = 1;
//         }
//         else {
//             status = -1;
//         }
//         return status;     
//     }
// };

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() - tempSampleTime > DS18B20_SAMPLE_INTERVAL)
    {
        getTemp();
        tempSampleTime = millis();
    }

    if (millis() -  metricPublishTime > Metric_Publish_Rate)
    {
        Serial.println("Publishing now.");
        publishData();
        metricPublishTime = millis();
    }
    
    if (isrFlag)
    {
      Particle.publish("photon/motion/detected", "1");
      isrFlag = false;
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
  isrFlag = true;
}

//void resetAlarm()
//{
//    Alarm alarm;
//    alarm.status = 0;
//}

//void raiseAlarm()
//{
//    Particle.publish("photon/motion/detected", "1");
//    resetAlarm();
//}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    Particle.publish("photon/temperature/celsius", String(celsius));
    Particle.publish("photon/temperature/fahrenheit", String(fahrenheit));
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        Serial.println(fahrenheit);
    }
}

I agree with @BulldogLowell, there is a lot of object construction/destruction going on in your code.

Whenever you write

  Alarm alarm;

You create a new object which is unaware of any state any previous instance of alarm had and it will be destroyed once your code leaves the function where you created it.

This was also the point of this sentence

I think you need to readup about scope of automatic variables and objects.
Meanwhile rather use a global instance of Alarm (or use static class fields).

1 Like

I think perhaps what I was trying to get at was that I wanted to do it in a way I could have a timer and not block the main loop with a delay.

Perhaps to elaborate this idea further if we consider that a motion trigger only triggers an alert to be fired every 5 seconds. So you cannot retrigger within five seconds. I have two thoughts on this, one I can probably work out which is to trigger a timer which I check with millis() and the other which I think I’ve sniffing around the idea of; is if there is a more advanced technique to have multiple threads running perhaps leveraging more within the FreeRTOS library (of which I have no experience). I’ve seen the cosa arduino library which is interesting, but wondered if something similar is possible with the photon?

Many thanks

Andy

I’d suggest before diving into any multithreading aproach is to get an in-depth understanding and good coding practice for sigle-threading.

Multithreading is a very powerful tool, but can hold traps and snares for you if you don’t exactly know what you are doing (shared resources, race conditions, synchronization, …).