Arduino and 1-wire

I tried to get a DS18S20 working but didn't get any further than the reset. That seemed to work but I never got any data from the sensor. I played around with the timing values but that didn't help me. The delayMicrosecond function seemed to work ok as far as I could measure it with my scope. So I guess filling in the delay's as mentioned in the datasheets should work.

Do you want to share your code? Or at least a read function?

Please let us know if you get it working! Here, or in my comments section.

Edit: My scope doesn't seem to trigger well & sharp unless I do one request at a time in the loop. It appears that the data line goes to around 3v when the conversion starts. I did use the required 4.7k pull-up resistor on the data line. And, I accidentally hooked it up backwards polarity once, oops!

/* DS18S20 Temperature chip i/o
 * ---------------
 *
 * See http://pdfserv.maxim-ic.com/en/ds/DS1820-DS1820S.pdf for the datasheet.
 *
 * (copyleft) 2006 by Derek Yerger - Free to distribute freely.
 *
 *
 */
// variable declaration
int onepinio = 7; // i/o
int pres; // just a temporary variable to store return data  

void ds_reset(void) {
  pinMode(onepinio, OUTPUT);  // Pull low for 500mcs
  digitalWrite(onepinio,0);
  delayMicroseconds(500);
  pinMode(onepinio, INPUT);   // Release for at least 500mcs
  pres=0;
  delayMicroseconds(500);
  while (pres = 0) {
    pres=digitalRead(onepinio);
  }
}
void ds_write1(void) {
  pinMode(onepinio, OUTPUT);
  digitalWrite(onepinio,0);
  delayMicroseconds(5);        // A "1" is when the line is pulled
  pinMode(onepinio, INPUT);    //     1mcs < t < 15mcs  
  delayMicroseconds(45);       // so the write slots are even
}

void ds_write0(void) {
  pinMode(onepinio, OUTPUT);
  digitalWrite(onepinio,0);
  delayMicroseconds(20);        // A "0" is when t > 15mcs (30 typical)
  pinMode(onepinio, INPUT);
  delayMicroseconds(30);        // evening up again...
}

int ds_readbit(void) {
  pinMode(onepinio, OUTPUT);
  digitalWrite(onepinio,0);
  delayMicroseconds(1);          // A "read slot" is when 1mcs > t > 2mcs
  pinMode(onepinio, INPUT);
  delayMicroseconds(2);          // Wait just a bit
  return(digitalRead(onepinio)); // return what we see on the line
}

void ds_writebyte(byte dsbyte) {
  byte mask;                            // How heavy is this bit?
  int cwt;
  cwt=0;
  for (mask = 0x01; mask; mask <<= 1) {  // Thanks to mellis - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1147888882
    if (mask & dsbyte) {                 // Or whoever they got it from...
      ds_write1();
    } 
    else {
      ds_write0();
    }
  }
}

int ds_readbyte(void) {
  byte mask;
  int cwt;
  cwt=0;
  for (mask = 0x01; mask; mask <<= 1) {  // Thanks again.
    cwt = cwt + mask * ds_readbit();
  }
  return(cwt);
}

void setup(void) {
  // initialize inputs/outputs
  // start serial port
  pinMode(onepinio,INPUT);
  digitalWrite(onepinio,1);
  Serial.begin(9600);
}

void loop(void) {
  ds_writebyte(0xCC);         // Skip ROM
  ds_writebyte(0x44);         // Supposed to work, but doesn't for me.
  pres=0;
  //pinMode(onepinio, OUTPUT);  // Maybe I was going to try parasite power?
  //digitalWrite(onepinio, 1);
  //delay(100);
  //digitalWrite(onepinio, 0);  // In thst case, be sure to turn it back to 0
  //pinMode(onepinio, INPUT);   // to turn off the internal pull-up resistor.
  while (pres = 0) {          //   The chip's supposed to return a 0 until the
    pres=ds_readbit();        // conversion's done (can't do in parasite mode)
  }
  ds_reset();
  ds_writebyte(0xCC);         // Skip ROM
  ds_writebyte(0xBE);         // Read Scratchpad
  int cby;                    // Call me lazy, or creative: "current byte"
  cby=0;
  while (cby <=8) {           // we need 9 bytes
    Serial.print(ds_readbyte(), HEX);
    Serial.print(" ");
    cby=cby+1;
  }
  Serial.println();
  delay(100);                 // Lets not flood.
}

Well, unfortunately it doesn't work for me either. But, at least I understand why my code didn't work.

I Keep getting the same line:
"AA 0 4B 46 FF FF C 10 87"
The only thing I changed in the code was adding a ds_reset() at the start of the loop(void). But it didn't make any difference. I tried a "read rom", (33h) command. Then the 64bit ROM code is read from the scratchpad. So, that works.
As you stated in the comment after the CONVERT (44h) command this probably doesn't work.

Well, unfortunately it doesn't work for me either. But, at least I understand why my code didn't work.

I Keep getting the same line:
"AA 0 4B 46 FF FF C 10 87"
The only thing I changed in the code was adding a ds_reset() at the start of the loop(void). But it didn't make any difference. I tried a "read rom", (33h) command. Then the 64bit ROM code is read from the scratchpad. So, that works.
As you stated in the comment after the CONVERT (44h) command this probably doesn't work.

Yes, that's all I get too :-/
All the digital operations work, but convert doesn't. I haven't had time to, but I'd like to read my ROM code and address the chip specifically when issuing the convert command. The datasheet isn't very clear and straightforward in describing when "Skip ROM" can be used.

Happy Holidays!

The datasheet isn't very clear and straightforward in describing when "Skip ROM" can be used.

No, they mention it is possible with only one slave on the bus. See page 9 "SKIP ROM", in the DS1820 datasheet. (you can follow your own link tot the datasheet)

I searched around on avrfreaks.net to see if there are similar problems. I couldn't find anything useful. I thought I read something about a similar problem.

Would the problem be on timing? How long will the temperature be in the scratchpad? Or would it take more time to finish writing it to the scratchpad than you wait with the while(pres=0) loop?

The datasheet says the chip will return "0" if it is requested, until the conversion is done. I think the temperature is stored in the scratchpad and shouldn't have to be read x amount of time after converting. Your guess is as good as mine :-?

You almost certainly have a DS18S20P on your hands. This is a variant that only uses parasitic power, the power lead is not connected in the package. You can verify this by using another pin to trigger a scope just before you write the 0x44, you will then see the 8 bits followed by a tragically sagging voltage on the data pin.

I ran out to the FET shop and got a VN2406L (the guy said "everyone buys these", good enough for this) and wired it to another pin like the data sheet shows and everything works now.

The only sad part about this is that now you have to blindly wait 750ms for the temperature to read instead of waiting just long enough.

The atmega8 io pins have a maximum rating of 40mA per pin... But I'm not seeing maximum power consumption for the converter while converting in parasite mode. I did see the voltage drop about half after the start conversion command, so it must be trying to do parasite power.

Enabling pull-up on the data line by the microcontroller didn't seem to fix the problem. Also, I didn't spend too much time on the oscilliscope trying to perfect timings in accordance with the microcontroller.

So the fet is required to supply the device, and in that case, I assume a second digital out could be used to control that pull-up. If it could be done with the USB's 500mA, what would be suitable for this application? I need to order a few, there are no FET shops around these parts.

With a 750ms delay on conversions, if you had numerous devices on a line, it wouldn't be a very quick operation, to say the least. When I saw one when ordering my arduino, I thought it might be an interesting thing to use, but thermistors are just so much cheaper! :slight_smile:

I'm missing something here, looking at the atmel source current spec (~20mA) and the ds18s20p current (~1.5mA) draw during a conversion it should be able to power a dozen temperature sensors on a single pin without a problem. Very odd. It is almost as if the Atmel is using its internal pull up resistor to go high instead of the buffer, but I don't see a combination of settings that would cause that. I'll try a bunch of resistors and make a current/voltage plot when I get back to m analog bench and see what those ports are really doing.

Ok, the world has not gone crazy. The Atmels really do provide >20ma on the pins and the DS18S20P does take about 1ma. The problem was in the timing. As soon as the DS18S20 receives the 0x44 it will start taking current for the conversion. Unfortunately, the write_byte function is sitting around for many microseconds with the pin in a high impedance state in order to provide timing margins. The DS18S20P crashes before the power gets turned on.

The solution is to not let the external pull-up bring the pin high after the low period of the final bit of the 0x44 is written, force it high with the output driver. Then after the 750ms conversion period passes flip the pin back to an input and let the external pull-up take over.

My little sensor is now happily converting away on my desk.

I'll clean up my changes to the one-wire code and put them up here soon.

That's good news jims! How do you connect the DS18S20? I have a resistor between +5V and the data pin. I found this somewhere in the avrfreaks forum.

I have the grounds connected, the DQ pin on the DS18S20P connected to the pin of the Arduino, and a 4.7kohm resistor from DQ to +5v as the pullup. It is good to connect the Vdd pin of the DS18S20P to ground also so if you get a non-parasite version (DS18S20) it will also work in parasite mode.

If you are going to use more than a few of these on the same pin you will need to use a pullup transistor and another pin, as shown in the DS18S20 datasheet, but for a few, say less than 10? of them the Arduino has enough current capacity.

The solution is to not let the external pull-up bring the pin high after the low period of the final bit of the 0x44 is written, force it high with the output driver. Then after the 750ms conversion period passes flip the pin back to an input and let the external pull-up take over.

Do you use the same pin for this? In yerg2k's code I bring the pin high immediately after the ds_writebyte(0x44) function. But it still doesn't work. I was wondering if it maybe takes too much time to finish the ds_writebyte() function and that you are doing someting different with an additional pin.

You almost certainly have a DS18S20P on your hands.

Hmm, overlooked this one but indeed I have the DS18S20P. So far I still couldn't find what I am doing wrong. I am using the 750 uS delay after the 44h write, then pulling the pin high. Why don't I see what's going wrong?

This http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=41771&highlight=ds18s20 thread explains something about connecting the DS18S20. To it seems it is about the powered sensor. Unfortunately, because I have the DS18S20P I can't connect to it like "kartman" explains.

:slight_smile: YES YES
Finally, it works. I'll do a quick clean up of the code and post it. Though I rewrote almost everything it was a hardware >:( problem after all. I don't really understand it, maybe someone can explain it to me. There are some other issues that I found out by trial and error. So, I am not there yet, but there is a start to get 1-wire working.

This program is specifically for the DS18S20P. But it is probably easier to get a non P(arasite) version working. To avoid misunderstandings let's start with the DS18S20P connections.

GND and Vdd to GND, Dq to pin 6 AND to pin 7 AND via a 4k7 resistor to +5V on arduino board.

Thanks to yerg2k to get me started and jims to get me thinking.
Comments are welcome.

/* DS18S20P Temperature chip i/o 
 * ---------------
 *
 * See http://pdfserv.maxim-ic.com/en/ds/DS1820-DS1820S.pdf for the datasheet.
 *
 * (copyleft) 2006 by Derek Yerger - Free to distribute freely.
 * (sorry Derek, great inspiration but I rewrote it completely)
 *  heavily modified by bigengineer
 * inspired by: http://microsyl.com/
 * and Dallas 1-wire datasheets
 *
 */
#define DQ 7  //data pin 
#define PU 6  //separate pin for pull up
#define pin13 13
#define VERSION 01

void setup(void) 
{
  // initialize inputs/outputs
  // start serial port
  pinMode(DQ,INPUT);
  pinMode(PU,INPUT);
  pinHigh();
  Serial.begin(9600);
}
byte reset()
{
  pinLow();
  delayMicroseconds(500);
  pinIn();
  delayMicroseconds(70);
  if (digitalRead(DQ) == LOW)
  {
    delayMicroseconds(500);
    return(1); 
  }
  return(0);
}
void WriteByte(byte data)
{
  byte i;
  for(i=0;i<=7;i++)
  {
    pinLow();
    if (data & 0x01) //write 1
    {
      delayMicroseconds(7);
      pinIn();
      delayMicroseconds(70);      
    }
    else //write 0
    {
      delayMicroseconds(70);
      pinIn();
      delayMicroseconds(7);
    }
    data>>=1;
  }
}
void convertTemp()
{
  byte i;
  byte data = 0x44;
 
  for(i=0;i<=7;i++)
  {
    pinLow();
    if (data & 0x01) //write 1
    {
      delayMicroseconds(7);
      pinIn();
      delayMicroseconds(70);      
    }
    else //write 0
    {
      delayMicroseconds(70);
      pinIn();
      delayMicroseconds(7);
    }
    data>>=1;
  }
  pullupHigh(); //pull pin 6 high for the conversion
  delay(750); //conversion time is 750 milliseconds
  pullupIn(); //switch off pin 6
  pinIn();
}
byte readByte()
{
  /* timing is critical here. But timing values are different
   * from the datasheets. These values are found by trial & error.
   * The delay's should be somewhere around 15 uS.
   */
  byte data=0;
  byte i;
  byte bit;
  for(i=0;i<8;i++)
  {
    pinLow();
    delayMicroseconds(1); // > 2 doesn't work
    pinIn();
    delayMicroseconds(1); // >5 doesn't work
    bit = digitalRead(DQ) & 0x01;
    data >>= 1;
    if (bit) data |= 0x80;
    delayMicroseconds(50); //doesn't seem to be necessary
  }
  return(data);
}

void pinHigh()
{
  pinMode(DQ,OUTPUT);
  digitalWrite(DQ,HIGH);
}
void pinLow()
{
  pinMode(DQ,OUTPUT);
  digitalWrite(DQ,LOW);
}
void pinIn()
{
  pinMode(DQ,INPUT);
  //pullupIn(); //om de resistor pull-up te laten werken
}

void pullupHigh()
{
  pinMode(PU,OUTPUT);
  digitalWrite(PU,HIGH);
  digitalWrite(pin13, HIGH);
}
void pullupLow()
{
  pinMode(PU,OUTPUT);
  digitalWrite(PU,LOW);
  digitalWrite(pin13, LOW);
}
void pullupIn()
{
  pinMode(PU,INPUT); //necessary for resistor pull-up
  digitalWrite(pin13, LOW);
}

void readRom()
{
  byte j;
  byte pad[9];

  reset();
  WriteByte(0x33);
  for(j=0;j<8;j++)
  {
    pad[j] = readByte();
  }
  for(j=0;j<8;j++)
  {
    Serial.print(pad[j], HEX);
    Serial.print(" ");
  }
  Serial.println("read rom");
}
void readScratchpad()
{
  byte j;
  byte pad[9];
  int msb,lsb;

  WriteByte(0xBE);
  for(j=0;j<9;j++)
  {
    pad[j] = readByte();
  }
  for(j=0;j<9;j++)
  {
    Serial.print(pad[j], HEX);
    Serial.print(" ");
  }
  Serial.print("read scratchpad  ");
  msb = pad[1];
  lsb = pad[0];
  if (msb <= 0x80)lsb = lsb/2;
  msb = msb & 0x80;
  if (msb >=0x80) lsb = (~lsb)+1;
  if (msb >=0x80) lsb = lsb/2;
  if (msb >=0x80) lsb = ((-1)*lsb);
  Serial.print("T =  ");
  Serial.print(lsb);
  Serial.print(" ");
    
}
void loop(void) 
{
  readRom();
  reset();
  WriteByte(0xCC);
  convertTemp();
  reset();
  WriteByte(0xCC);
  readScratchpad();
  Serial.print("version: ");
  Serial.println(VERSION);
   delay(1000);                 // Lets not flood.
}

[edit]changed typo that cosinekitty discovered[/edit]

This line of code scares my kitty cat :wink:

  if (digitalRead(DQ == LOW))

This compares DQ to LOW (7 == 0), which evaluates to false, or 0, then does a digitalRead of pin 0 and asks whether it returned HIGH or not.

I think you meant:

  if (digitalRead(DQ) == LOW)

But hey, maybe it works anyway... ????

I've finished. I packaged the 1-wire functions into a library for Arduino. It includes the read and write byte functions, reset, device enumeration, and CRC computation. The comments about how one might use it are in the library source. Sadly, it won't fit in a post anymore.

You can pick up the files at http://core.federated.com/~jim/onewire/

Create an arduino-0007/lib/targets/libraries/OneWire/ directory and put them in there. You will probably need to quit the arduino program and restart to get it to notice. The test.c program doesn't go in the library, it was a little test program you might find useful.

The test is working fine on my unit with 4 DS18S20s connected.

Physically, I have all the DQ lines connected to my IO pin, all the grounds connected to a ground, and 4.7k ohm resistor from a Vcc to the DQ lines. It would be best to connect all the remaining pins of the DS18S20s to either Vcc or GND, then a regular DS18S20 (not a -P) would still work in either normal or parasite mode depending how you connected it.

If you are going to have any kind of wire run I would suggest looking at some of the ESD protection variations (resistor in series with IO pin and 5.5v Zener diode to ground) to keep the stress down when bad things happen. Likewise, I think it would be possible to make this work using the internal pull-up of the Arduino, but decided that would be a needless stress with there is a short, and the resistor is a bit too big so it would be bad on longer cable runs and possibly not able to supply parasite power.

Physically, I have all the DQ lines connected to my IO pin, all the grounds connected to a ground, and 4.7k ohm resistor from a Vcc to the DQ lines. It would be best to connect all the remaining pins of the DS18S20s to either Vcc or GND, then a regular DS18S20 (not a -P) would still work in either normal or parasite mode depending how you connected it.

Strange, my code started working when I used another pin to supply current to Dq during the temperature conversion.

I am very glad that you have the device enumeration already working, that saves me a lot of time!

I will try it and report back.

Well jims, your code is working! I don't understand why I needed an extra pin to get my code working but your code works with just pin 7 and GND connected.

At the moment I have 3 sensors connected and all 3 give a temperature reading. Your library should become a standard library.