Feb
4
One Wireduino made easy
16 years ago, at the start of February | 12 Comments
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() > 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 < 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,&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 be downloaded, 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.
Tagged with: 1-Wire • arduino • hardware • opensource • software
February 4, 2009 21:46
Comments
12 Comments so far
Current Electricity Use (15min)
iPhone/Webkit RSS Reader
Links
- automated home
- Automated It Technology News
- awooga!!!
- LinITX
- My Acer page
- My Asterisk pages
- My Work in progress (old)
- Noble Race Car
- openmoko / neo 1973 wiki
- planet openmoko
- Spadgecock Cumpants
So a couple questions – how are you getting the data into cacti from the arduino? That looks cool. Also – how many devices can be on the 1-wire bus? I’ve seen a bit of conflicting information on this – one source uses a phanderson one wire interface chip, but I’ve done a little 1-wire with just the arduino. I’d love to be able to hang 20 sensors off an arduino.
Josh,
I’ve hope to have answered your question on how to get the data into cacti by adding to the bottom of the article.
As for the number of devices, as yet, I’m unsure but I’m going to get my hands on some more DS1820’s to see when/if any issues occur. I have to say though getting >=20 is not going to happen that soon, perhaps another 5-10. So far I’m testing in a single chain, things may be different if using multiple chains off different io ports, switching between them as needed. More experimentation is required. Running out of memory is likely to be the main problem.
Man, I can’t get this to work at all. I haven’t done any C programming in years, so it’s probably me instead of the code. When I run the sample.c test program, it just spits out “No more addresses” over and over, unless I disconnect the DS1820. In that case it prints “R=0 0 0 0 0 0 0 40 CRC is not valid!” over and over.
It seems so simple, what am I doing wrong? Looking at the OneWire.cpp file, I would swear that the search method wants an 8-bit byte pointer for input, but you’re sending it a pointer to an 8-element byte array. But like I said, I haven’t done any C in years. Any idea what I’m doing wrong?
I figured it out – I didn’t pay close enough attention to the schematic at the top of your page. I had assumed that the internal pullup resistors were being used, and left out the one you showed in the diagram. Doh!
Sorry I didn’t get to you in time David but I wasn’t near a PC 🙁
Hi,
thanks for a nice piece of code, works just fine.
I believe there is a small bug in line 9&10 😉
You can get OneWire library from here http://homepage.mac.com/wtpollard/Software/FileSharing7.html, just unpack it to ‘…\hardware\libraries’ folder inside Arduino main folder.
Thanks for spotting that one Mike. Looks like I’d lost those while editing for some reason.
Hi
Good resource,
How temps can be measured on one bus??
I’m not sure what you’re asking here Trevor, I think you might be wanting to daisy chain more sensors. If that is the case then first of all you don’t need to change any code at all. The code will scan for multiple chips and return the values for each. The 1-wire id is printed followed by its value . For the wiring you simply repeat the connections as per the first diagram. I take 2 wires GND and DQ to the next chip from the first and link the VDD to GND near the chip. Hope that clears it up for you..
Hi,
Yes, your answer was what I was asking for.
I’ll give it a try and see how I get on. I thought you might have a practical knowledge of what number of 1-wire devices can be placed on the bus.
Thanks,
Trevor
Trevor,
The limitation on how many 1-wire devices can be in a chain is not so much the bus it’s more to do with the memory of the arduino. I’ve worked around that problem because I’m actually using wireless nodes with a number of 1-wire devices attached. Each node handles a few sensors for the area they are in. This saves me from having to drill holes in walls or ceilings. I’ll write it up but essentially it’s just xBees and 1-wire chips on an arduino.
If you want to add a lot of 1-wire devices and you are not using the arduino then you might want to look at not using parasitic power as per my diagram. Using local power will allow for longer cable runs and so more sensors on the bus
In your Arduino code you need to add a leading zero to the frac variable if it is less than 100.
Thanks a lot for documenting all this, it has allowed me to get something that works as a starting point.