Emulated Onewire DS18b20 unable to be read by Teltonika FM1120 device

Hi,

I am currently working on making an emulated temperature sensor (DS18B20) by using arduino uno. I found a library that seems to work.

The emulated sensor can be read when it is connected to another arduino with a code that reads onewire devices. Below is the code:

#include <OneWire.h>

// OneWire DS18S20, DS18B20, DS1822 Temperature Example
//
// http://www.pjrc.com/teensy/td_libs_OneWire.html
//
// The DallasTemperature library can do all this work for you!
// http://milesburton.com/Dallas_Temperature_Control_Library

OneWire  ds(15);  // on pin 10 (a 4.7K resistor is necessary)

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  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, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
}

However, when it is connected to Teltonika FM1120 device’s onewire data pin ( pin 8 ) together with common ground, it does not show any readings at the server.

http://plantron.gr/sites/plantron.gr/files/pdf/FM1120%20User%20Manual%20v1.12.pdf

Below is my emulated sensor code:

#include "OneWireHub.h"
#include "DS18B20.h"  // Dual channel addressable switch

#define OneWire_PIN 9

OneWireHub * hub = 0;

DS18B20 * fMS; 
float temp = 0;
void setup() {
  // Debug
  Serial.begin(9600);
    
  // Work - Digital Thermometer
  fMS = new DS18B20( 0x28, 0xFC, 0x00, 0x49, 0x35, 0x32, 0x12  );    

  // Setup OneWire
  hub = new OneWireHub( OneWire_PIN ); 
  hub->elms[0] = fMS;
  hub->calck_mask();
}

void loop() {
  // Set temp
  fMS->settemp(10);
  
  // put your main code here, to run repeatedly:
  hub->waitForRequest(false); 
}

I have also taken an oscillogram when the Teltonika device is connected to the emulated sensor; also when the emulated sensor is connected to the arduino reader ( reads onewire devices ).

Emulated sensor to arduino reader:

Emulated sensor to Teltonika device:

It seems that the emulated sensor is not sending the presence pulse after the Teltonika device has sent the reset pulse. Any ideas or comments? Thanks in advance.

OneWireHub.cpp (22.3 KB)

OneWireHub.h (2.24 KB)

DS18B20.h (320 Bytes)

DS18B20.cpp (2.67 KB)

Interesting.

Can someone help me with the coding?

When I read your first post, I realized OneWireHub was a fairly complicated library. Its purpose is to act as a proxy master or “hub” for multiple, simulated one-wire devices. Without setting up an Arduino master with the temperature example, and a second Arduino as a OneWireHub, I can only suggest a few things that I would try. In priority order, they are:

  1. The first thing I noticed is that you only set up one device on the hub. If you only need one device, there are OneWire slave libraries that are much simpler than the OneWireHub library. They allow an Arduino to emulate one DS18B20. Finding a problem in that code would be much easier.

  2. There are several microsecond interval calculations in the library that do not handle micros() rollover correctly:

    time_stamp = micros() + 540;
    
    //Wait for the rise on the line up to 540 micros
    while (DIRECT_READ(reg, mask) == 0) {
        if (micros() > time_stamp) {

I doubt this is your specific problem, but it does indicate that the author may have other signed/unsigned math errors.

  1. There are several places that can permanently hang:
    while (!DIRECT_READ(reg, mask));

I doubt this is your problem, because you have 'scope traces to confirm that you should have received the reset signal.

BTW, I have not actually looked at your 'scope .MOV files, because I rarely take the time to download things, and certainly not from untrusted sources. I much prefer embedded JPGs: attach JPG, post, copy JPG attachment URL, modify post, insert URL in the post body with an
** **[img]** **
tag.

Without looking at the trace, I can’t tell you if the voltage levels or timings (i.e., rise time) are “significantly” different.

  1. If I were doing this, my next step would be to figure out what the OneWireHub was doing. Although you can’t use debug Serial.Print in most places, there are many other techniques to choose from:

a) Use a logic analyzer to monitor multiple extra Digital pins that you toggle as various parts of the sketch are executed. If you don’t have an analyzer…

b) Use a second scope channel to monitor an additional Digital pin. Toggle it as you cross various parts of the code.

c) Set a global variable (or variables) to different values from inside the library. Serial.print the value (or values) when you get back to the main loop().

d) Build a DAC resistor ladder for a couple of Digital pins. For “n” pins, you will get 2n analog values that you can see on the second channel. If you don’t have a second channel…

e) Disconnect from the OneWire bus and try (b) and (c), assuming that it is repeatably hanging in the same place (or missing the reset entirely).

f) Use the LED pin and/or connect LED+resistors to extra Digital pins. Toggle them as you cross various parts of the code. If the code is hanging, the LEDs will be left in the last state you set. Again, for “n” LEDs, you could have “n” states or 2n states.

Cheers,
/dev

Hi /dev,

Thanks for your reply and your time for reading my post.

The first thing I noticed is that you only set up one device on the hub. If you only need one device, there are OneWire slave libraries that are much simpler than the OneWireHub library. They allow an Arduino to emulate one DS18B20. Finding a problem in that code would be much easier.

I am currently figuring out a way to communicate with the Teltonika Device's OneWire bus (Teltonika is the master) - so yes, I only need one device currently. I have tried finding other libraries that could emulate DS18B20. I could only find two libraries - one is OneWireHub, the other is OneWireSlave. The OneWireSlave library does not seem to work. I tried testing it(OneWireSlave) with another arduino as the reader but it does not show any readings. Please introduce me some OneWire slave libraries.

4) If I were doing this, my next step would be to figure out what the OneWireHub was doing. Although you can't use debug Serial.Print in most places, there are many other techniques to choose from:

a) Use a logic analyzer to monitor multiple extra Digital pins that you toggle as various parts of the sketch are executed. If you don't have an analyzer...

b) Use a second scope channel to monitor an additional Digital pin. Toggle it as you cross various parts of the code.

c) Set a global variable (or variables) to different values from inside the library. Serial.print the value (or values) when you get back to the main loop().

d) Build a DAC resistor ladder for a couple of Digital pins. For "n" pins, you will get 2n analog values that you can see on the second channel. If you don't have a second channel...

e) Disconnect from the OneWire bus and try (b) and (c), assuming that it is repeatably hanging in the same place (or missing the reset entirely).

f) Use the LED pin and/or connect LED+resistors to extra Digital pins. Toggle them as you cross various parts of the code. If the code is hanging, the LEDs will be left in the last state you set. Again, for "n" LEDs, you could have "n" states or 2n states.

I did debugging mostly using Serial.print(). I have no logic analyzer. I will try the other ways you recommended.

Thanks, wongsm7.

Please introduce me some OneWire slave libraries.

Yes, OneWireSlave is the one I was thinking of, but there are several versions. There's some recent discussion here, although it may be Teensy-specific.

Perhaps older info in post #87 here... it points to a Romanian blog about getting it to work. Google translate worked pretty well, and there are a couple of ZIP files there.

That's just the software, though. I suspect there are timing or signal differences, perhaps due to termination inside the Teltonika (remove your 4.7k) or not inside (add a 4.7k). You haven't shown us your schematic and/or a photo of the wiring, so we can't help you with a hardware problem. A JPG of the reset pulse on the scope might reveal the difference - take a pic if your scope can't upload a screen capture. Be sure to resize to something appropriate, 800 to 1024 pixels wide is plenty. And embedding it would be nice... :)

There's some discussion here regarding wiring, with links to here (German).

Cheers, /dev

Some update here.

I tried using the OneWireHub Library to emulate DS2401 (Silicon Serial Number). I am not very sure what does DS2401 do, but its output seems to be like the iButton. I used the example code there.

http://pdfserv.maximintegrated.com/en/ds/DS2401.pdf

#include "OneWireHub.h"
#include "DS2401.h"  // Serial Number
//#include "DS18B20.h" // Digital Thermometer
//#include "DS2405.h"  // Single adress switch
//#include "DS2408.h"  // 8-Channel Addressable Switch
//#include "DS2413.h"  // Dual channel addressable switch
//#include "DS2423.h"  // 4kb 1-Wire RAM with Counter
//#include "DS2433.h"  // 4Kb 1-Wire EEPROM
//#include "DS2438.h"  // Smart Battery Monitor
//#include "DS2450.h"  // 4 channel A/D
//#include "DS2890.h"  // Single channel digital panemtiometer
// Variables will change:
OneWireHub * hub = 0;
void setup() {
  // Debug
  Serial.begin(9600);
  
  hub = new OneWireHub(9);

  // put your setup code here, to run once:
//  hub->elms[0] = new DS18B20( 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    // Work - Digital Thermometer  
 hub->elms[1] = new DS2401 ( 0x01,0x00, 0x90, 0x78, 0x56, 0x34, 0x12);    // Work - Serial Number
//  hub->elms[2] = new DS2405(  0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    //      - Single adress switch
//  hub->elms[3] = new DS2408(  0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    //      - 8-Channel Addressable Switch
//  hub->elms[4] = new DS2413(  0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    // Work - Dual channel addressable switch
//  hub->elms[5] = new DS2423(  0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    //      - 4kb 1-Wire RAM with Counter
//  hub->elms[6] = new DS2433(  0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    //      - 4kb 1-Wire RAM with Counter
//  hub->elms[7] = new DS2438(  0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    //      - Smart Battery Monitor
//  hub->elms[0] = new DS2450(  0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    //      - 4 channel A/D
//  hub->elms[1] = new DS2890(  0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );    // Work - Single channel digital potentiometer

  Serial.println( hub->calck_mask() );
}

void loop() {
  // put your main code here, to run repeatedly: 
  hub->waitForRequest(false);
  
}

And I tried reading it with another arduino. It could read the serial number. My Teltonika Device could read the emulated DS2401 too.

Yes, OneWireSlave is the one I was thinking of, but there are several versions. There's some recent discussion here, although it may be Teensy-specific.

Perhaps older info in post #87 here... it points to a Romanian blog about getting it to work. Google translate worked pretty well, and there are a couple of ZIP files there.

That's just the software, though. I suspect there are timing or signal differences, perhaps due to termination inside the Teltonika (remove your 4.7k) or not inside (add a 4.7k). You haven't shown us your schematic and/or a photo of the wiring, so we can't help you with a hardware problem. A JPG of the reset pulse on the scope might reveal the difference - take a pic if your scope can't upload a screen capture. Be sure to resize to something appropriate, 800 to 1024 pixels wide is plenty. And embedding it would be nice... :)

There's some discussion here regarding wiring, with links to here (German).

Does the OneWireSlave Library only work for Teensy? Does it work with Arduino Uno?

Well the connections are simple:

(Arduino Uno) (Teltonika) Arduino digital pin 9 - 1-wire Data ( pin 8 ) GND - GND

Here’s the link to the image.

DS2401.cpp (465 Bytes)

DS2401.h (226 Bytes)

OneWireHub.cpp (22.4 KB)

OneWireHub.h (2.24 KB)

Here's the link to the image.

There's some problem with that link, I can't seem to embed it in my reply. Oh, I see you tried to do that, too.

Please attach the image to your post, directly from your machine, not dropbox (just like the CPP files you attached). Enter some text, then press "Post". After your post appears in this thread, right-click over the attached JPG at the lower left of your post, and "Copy link address" or "Copy URL". Then press "Quick modify" and you can edit your post. Insert the following:

   [img]paste URL here[/img]

Then press "Save", and the image should appear where the IMG tags were.

Does the OneWireSlave Library only work for Teensy?

No, it was originally for the Uno, Mega etc., but not the Teensy; it is a different processor. The latest discussion was about getting it to work on the Teensy as well. There were several versions of code in that thread, but I'm not sure how they compare to the github version. Just something to try.

Well the connections are simple:

(Arduino Uno) (Teltonika) Arduino digital pin 9 - 1-wire Data ( pin 8 ) GND - GND

This is probably the problem. The OneWire bus requires a 4.7k pullup resistor (read here and here). Here is a blog post about trying to eliminate the resistor when the Arduino is a master, not a slave. It does describe the problem nicely, though, and has lots of links and images.

At this point, adding this resistor (across pin 9 and 5V) is probably easier than modifying OneWireHub to use the internal pullup. If you read that blog post, you'll see that the rise time with the internal pullup may not be quick enough, depending on your specific wiring. The resistor value can be fine-tuned for your situation by looking at the scope trace. This is probably a good thing to do, since you may not know what's really inside the Teltonika device.

My Teltonika Device could read the emulated DS2401 too.

That's a little surprising, but it may indicate that the rise time is at the limit of the 1-wire spec.

Cheers, /dev

Thanks again for the reply.

No, it was originally for the Uno, Mega etc., but not the Teensy; it is a different processor. The latest discussion was about getting it to work on the Teensy as well. There were several versions of code in that thread, but I'm not sure how they compare to the github version. Just something to try.

I have tried the OneWireSlave Library again. The example code given already has some compilation errors. It does not seem to be reliable. The ISR's name is already being used and declared in the header file; and it was used again in the codes.

At this point, adding this resistor (across pin 9 and 5V) is probably easier than modifying OneWireHub to use the internal pullup

I have tried connecting a real DS18B20 to the Teltonika Device (without pullup resistor). The Teltonika Device could read it flawlessly. This means that there is a pull-up resistor inside the device?

I have also tried using a +5V pullup resistor (4.7k ohms) for the emulated DS18B20. Well, the Teltonika Device still does not show any readings... The DS2401 that I have mentioned in post #6; the Teltonika Device could read it with the pullup and without the pullup surprisingly.

OneWireSlave has some compilation errors.

Recent IDE changes have also broken many libraries. Sigh.

there is a pull-up resistor inside the device?

Yes. Adding the extra resistor just makes the rise time quicker.

Because the DS2401 works with or without the resistor, with another Arduino or the Teltonika, I think that leaves either a HW/SW timing difference or a DS18B20.cpp error. However, the fact that another Arduino can read the emulated DS18B20 implies that DS18B20.cpp can work. This points back to a timing difference.

There are two possibilties: either the code has an explicit error that causes marginal timing (some combinations don't work), or the code looks ok, but doesn't perform as intended. Either way, you will need scope traces to see the the timings.

I would suggest comparing the reset pulse and response (or lack thereof) for:

1) emulated DS2401 to Teltonika (no resistor), 2) emulated DS18B20 to Arduino (resistor required), 3) emulated DS18B20 to Teltonika (no resistor), and 4) real DS18B20 to Teltonika (no resistor).

This will tell you what timings work with the Teltonika, and what timings are generated by the emulated devices. I suspect there is a difference in the reset pulses between 2 and 3, and between 3 and 4.

I'm really just guessing without the traces. If you want us to look at them, either attach the JPGs as described earlier, upload them to an image-sharing site (Picasa, Photobucket, or Imageshack) and embed them with [img] tags, or upload your videos to YouTube.

Which IDE version are you using? The newest versions include a different compiler that may be generating different timings from when OneWireHub was originally written.

Using the techniques previously listed, have you identified why OneWireHub.cpp does not respond?

And do you have more than one channel on your scope?

Cheers, /dev

Wow thanks again for the quick reply :)

I think that leaves either a HW/SW timing difference or a DS18B20.cpp error

What's HW/SW?

I would suggest comparing the reset pulse and response (or lack thereof) for:

1) emulated DS2401 to Teltonika (no resistor), 2) emulated DS18B20 to Arduino (resistor required), 3) emulated DS18B20 to Teltonika (no resistor), and 4) real DS18B20 to Teltonika (no resistor).

Well, my office doesn't have a scope so gotta borrow one. I would try that soon.

Which IDE version are you using? The newest versions include a different compiler that may be generating different timings from when OneWireHub was originally written.

1.6.1 I believe.

Using the techniques previously listed, have you identified why OneWireHub.cpp does not respond?

It seems that after the presence pulse has been sent, the Teltonika Device does not seem to give a match rom command (0x55). It keeps giving 0xF0. There might be problem with the presence pulse or timing issues? I have tried playing with some of the timings... it is still stuck at giving me 0xF0 :'(

And do you have more than one channel on your scope?

The one I borrowed previously has more than one channel :)

Will update you again. Thanks for your help!

What's HW/SW?

Hard*Ware/SoftWare, although we're actually talking about HW (DS18B20, pullup resistors, wires) and **FirmW*are on an Arduino... something between Hard (not reprogrammable) and Soft (runs in RAM), because sketches run from Flash memory.

It seems that after the presence pulse has been sent, the Teltonika Device does not seem to give a match rom command (0x55). It keeps giving 0xF0.

0xF0 is actually the SEARCH ROM command. It is beginning the enumeration process to find all the 1-wire devices. 0x55 is the MATCH ROM command that will be used to address one device. If the Arduino does not respond to the SEARCH ROM command, the Teltonika will never talk to it with the MATCH ROM command. Read here and here. Now the question is why the Arduino did not respond. Does the code recognize the SEARCH ROM code? Does it try to respond in the OneWireHub::search() method?

The one I borrowed previously has more than one channel

Great, you can monitor extra digital pins for debugging.

Cheers, /dev

Hey, there is an hub->errno you can look at for an error code.

Sorry for the late reply.

Hey, there is an hub->errno you can look at for an error code.

I have checked the errno, it's 7.

Now the question is why the Arduino did not respond. Does the code recognize the SEARCH ROM code? Does it try to respond in the OneWireHub::search() method?

I have attached my .cpp file. I have used serial.print to verify each part of the code.

F0
7
RESTART
F0
7
RESTART
F0
7
RESTART
F0
7
RESTART

This is what it shows on the serial when I connect it to the Teltonika device with and without the pull-up.

F0
errno==0
waitreset
waitreset
waitreset
waitreset
waitreset
waitreset
55
recvandprocesscmd
F0
errno==0
waitreset
waitreset
waitreset
waitreset
waitreset
waitreset
55
recvandprocesscmd

This is shown on the serial when I connect it with the arduino reader with and without the pull-up.

OneWireHub.cpp (22.6 KB)

Hehe... nothing attached. Got it.

Ok, it looks like it correctly receives the SEARCH ROM command and calls ::search.

::search then prepares to respond to the enumeration algorithm. This is a kind of binary search initiated by the master. As long as slave devices respond, the master works its way down the binary tree of the address space.

In your case, ::search is trying to respond to the search by calling sendBit. This protocol is really a "dance" between the master and the slave. Master prompts, slave responds. But the secret sauce is that the line must float high within a certain time. sendBit waits for the line to float high by calling waitTimeSlot. It doesn't happen, or it doesn't happen soon enough. When the waiting retries are used up, waitTimeSlot sets errno 7 and everything gives up.

Remember I'm just guessing from reading the code... I would add and remove prints to make sure that's really what's happening, or use the other techniques to guarantee that it isn't in a different part of the code.

As far as what to do, you could try changing things related to waitTimeSlot, like this define:

#define TIMESLOT_WAIT_RETRY_COUNT microsecondsToClockCycles(120) / 10L

This is a curious value though. As a reader, I would have thought one of two things: (1) the retry loop is hard-coded to go 10 times, with each iteration waiting for 12uS; or (2) the retry loop has been measured to take 10 clock cycles, and this is how many loops can be executed in the 120uS (max) time slot.

However, waitTimeSlot does not iterate 10 times, so (1) is wrong. OTOH, if (2) was intended, that would make it susceptible to changes in the generated code. Oh, say, if a new version of the compiler is released. :-/

Maybe you should print out the TIMESLOT_WAIT_RETRY_COUNT. Then hard code it to that value, plus 10%:

#define TIMESLOT_WAIT_RETRY_COUNT 211L

Or 20% more... Those scope traces would really help here.

To me, there are several problems with this library: 1) As I mentioned earlier, time calculations do not handle rollover. 2) I don't think it's a good idea to hard-code the number of instruction cycles. 3) It uses 768 bytes of RAM to deal with enumerating multiple simulated elements, plus 40 bytes for a local "stack" used during a traversal. I don't think I've figured out all that code, though.

Oh well, without debugging OneWireSlave, watcha gonna do?

Cheers, /dev

Oh well, without debugging OneWireSlave, watcha gonna do?

Does OneWireSlave really work? I have tried connecting it to another arduino... can't even read the temperature...

define TIMESLOT_WAIT_RETRY_COUNT microsecondsToClockCycles(120) / 10L

What is L anyways?

Here are the links to the oscillograms.

DS2401 to Teltonika: https://youtu.be/TsU9BnoawxE

Real DS18B20 to Teltonika: https://youtu.be/aG0tkIIJOhM

Emulated DS18B20 to Teltonika: https://youtu.be/1DGFm7sJA2I

Emulated DS18B20 to Arduino: https://youtu.be/xpNxoUtLPLo