18 Jun 2009

 

One Wireduino made easy (from http://blog.automated.it/)

For a very long time now I’ve been playing with the Arduino boards but it wasn’t until I recently discovered some DS1820 1-wire chips in my parts box that I started even thinking about 1-wire stuff. Connecting 1-wire devices to the Arduino is, well, amazingly simple. Using the sample circuit from the datasheet we can see the connections are few and far between.

Please note that this is the corrected circuit, previously the VDD was not tied to ground. While the circuit would work it would only give reliable results on very short runs. This circuit allows longer runs to the sensor.

Coding this up is also made extremely easy with the OneWire library. I’ve put together a little application below that will read all of the 1-wire devices on a bus. If you press L in the Arduino IDE serial monitor it will list all the 1-Wire device id’s and if you press T you’ll get the id and temperature reading.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* Simple 1-wire device reading   Pass T over serial to get temperatures Pass L to list 1-wire devices   */
#include <OneWire.h>
#include <stdio.h>
 
#define CONVERT     0x44
#define READSCRATCH 0xBE
#define SKIP_ROM    0xCC
#define MATCH_ROM   0x55
 
OneWire  ds(10);  // The DS18S20 is connected on pin 10
int ledPin = 13;  // flash an led on 13 - we all like flashing lights
 
void setup(void) {
  // initialize inputs/outputs
  // start serial port
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);  //we'll use the led to output a heartbeat
}
 
void loop(void) {
  int incomingByte = 0;
  if (Serial.available() &gt; 0) {
		// read the incoming byte:
		incomingByte = Serial.read();
 
		// say what you got:
		//Serial.print("I received: ");
		//Serial.println(incomingByte, DEC);
                if (incomingByte == 84) {
                  getStuff();
                  Serial.print("\r\n^");
                } else if (incomingByte == 76) {
                   listDevices();
                }
	}
 
}
 
void listDevices(void) {
 
  byte addr[8];
 
  ds.reset_search();
 
  while(ds.search(addr)) {
 
     digitalWrite(ledPin, HIGH);   // sets the LED on
 
     if ( OneWire::crc8( addr, 7) == addr[7])   {
         if ( addr[0] == 0x10) {
            // Make sure it is a DS18S20 device
            char buffer[512];
            sprintf(buffer,"Id:%02x%02x%02x%02x%02x%02x%02x%02x\r\n",
                       addr[0], addr[1], addr[2], addr[3], addr[4],
                       addr[5], addr[6], addr[7]);
              Serial.print(buffer);
          }
      }
  }
 
    Serial.print('^');
}
 
void getStuff(void) {
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];
  char buff[15];
  float real_temp;
 
  float temp_count;
  float read_temp; 
 
  ds.reset_search();
 
  while(ds.search(addr))  {
 
     digitalWrite(ledPin, HIGH);   // sets the LED on
 
    if ( OneWire::crc8( addr, 7) != addr[7])  { // Check CRC is valid
        Serial.print("CRC is not valid");
    }
 
    if ( addr[0] != 0x10) {   // Make sure it is a DS18x20 device
      Serial.print("Device is not a DS18x20 family device.");
    }
 
    ds.reset();    // Reset device
    ds.select(addr);     // Select device
    ds.write(CONVERT,1);   // Issue Convert command 
 
    delay(1000);     // maybe 750ms is enough, maybe not
    digitalWrite(ledPin, LOW);    // sets the LED off
 
    present = ds.reset();  // Reset device
    ds.select(addr);  // Select device
    ds.write(READSCRATCH);  // Read Scratchpad
 
    for ( i = 0; i &lt; 9; i++) {  // we need 9 bytes
          data[i] = ds.read();
    }
 
    if(OneWire::crc8( data, 8) == data[8]) {  // Check CRC is valid
 
      // CRC is ok
      // Divide the temperature by 2 - note the » and
      // « need to be replaced with double < and >
      // wordpress kills the server when you try to 
      // save otherwise.
      read_temp=((data[1]«8) | data[0]) » 1 ;
     // Convert to real temperature
      temp_count=float(data[7] - data[6])/(float)data[7];
      real_temp = ((float)read_temp-0.25)+temp_count;
 
       // Convert float to ascii
      tempToAscii(real_temp,buff);
      char buffer[512];
      sprintf(buffer,"%02x%02x%02x%02x%02x%02x%02x%02x %s\r\n",
                  addr[0], addr[1], addr[2], addr[3], addr[4],
                  addr[5], addr[6], addr[7], buff);
 
      Serial.print(buffer);
 
    } else    {
       Serial.println("CRC Failed");
    }
  }    
 
void tempToAscii(double temp, char *buff) {
  int frac;
  //get three numbers to the right of the decimal point
  frac=(unsigned int)(temp*1000)%1000;
  itoa((int)temp,buff,10);
 
  strcat(buff,".");
  //put the frac after the decimal
  itoa(frac,&amp;buff[strlen(buff)],10);
}

There’s also a copy of the .pde file that you can download if you feel more inclined. It’s not an exact copy, I removed the commented out xml for the above version. It’s worth pointing out that this circuit is using what is called parasitic power, which means your cable run should be shortish. If you want to use an external power supply then pin 3 (VDD) on the DS18S20 should go to your external supply. This would allow for much longer runs of cable. Below is an example graph generated by cacti with 2 DS1820’s connected the the arduino. As you can see the temperatures aren’t exactly the same but this is to be expected since the accuracy of the DS1820 is +/- 0.5 ° C

The breaks in the graph were caused my me removing the USB plug from the PC it was connected to so that I could make some minor alterations to the circuit.

Josh asked how I was getting the input for Cacti, here’s the perl script I use

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/perl
 
$output = `echo -en "T" >>/dev/ttyUSB0 && cat /dev/ttyUSB0`;
 
@lines = split(/\r\n/,$output);
$t=1;
 
foreach $line (@lines) {
 
       @spl = split(/\s+/,$line);
 
	$lc = @spl;
 
	if ($lc gt 1) {	
                $node=$spl[0];
		$val=$spl[1];
 
		print "Temperature_$node:$val ";
	}
$t++;
}

This outputs Temperature_x:n where x is the 1-Wire id and n is the temperature. It could probably do with tidying up and if you wanted to you could pass the 1-Wire id to the script to and only output the temperature for that particular 1-Wire device. A copy of the exported Cacti template can bedownloaded, I’m hoping that you just need to import that into your Cacti after placing the script in the scripts directory. Just remember, under linux, if you unplug and replug in your Arduino you may need to set the tty port speed to 9600 (or whatever you put in your Serial.begin(); ). You can use the following command for that;

1
2
3
 
 
stty -F /dev/ttyUSB0 cs8 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts

Make sure that ttyUSB0 is what your Arduino is connected to first. If not you will need to change it in the perl script and on the line above.

From: http://blog.automated.it/2009/02/04/one-wireduino-made-easy/