Jump to content
43oh

One wire temp sensor project DS18B20


Recommended Posts

Plenty written in the past, but here is my project...

 

This isn't the final product. A number of the pending issues noted in the docs have been taken care of, but not all. I'm posting it here to highlight a few features (good and bad) when dealing with these sensors in a nontrivial project.

 

Three parts here: One is dealing with the one wire comm, one for conversion from degC to degF, and one for producing decimal output. No snippets for the first part, as the length would be excessive. Full project is in the attachment if you want to see the code.

 

The project is an 8 channel temp monitoring system, and later revision includes solid state relay's for control of vent fans, humidity, and hopefully, eventually, heating system monitoring.

 

My house is old. And poorly insulated. And poorly ventilated. I have made major improvements since I bought it, but there is still a long way to go. It will never be an Energy Star home, but, then again, having worked in several, most Energy Star homes aren't a year after construction. The system is intended for monitoring temps throughout the structure (interior in several places, attic, basement), outdoors (north side and south side), and humidity, displaying the temps for convenience, and controlling a couple vent fans (in the attic and the basement) to manage humidity and perimeter temperature, both for comfort and to protect the structure. Outputs for logging on a PC are included in the operating version. If I can spare the pins, a 32KHz crystal is in the future so the device can provide timestamps and schedule readings, but that is still in flux.

 

I started with 8 channels of temp sensing. I decided to put each sensor on its own line, rather than use the device ID addressing, because the code is a lot simpler, and takes a lot less space. Reading the device ID's will take a lot of the code space in a G series device, as well as a lot of RAM (or EEprom) to store the device ID's. When the rest is done, I may see if I can fit device addressing in. Or I may add a second MSP430 and split sensing duties. An additional benefit of using separate lines for each sensor is the ease of permitting hot swapping of the sensors. Unplug one and it is dropped on the next read cycle. Plug one in, and it is picked up. Handy, since I have a bunch of extra sensors around for things like foundation and crawl space temps, that are not a major concern unless the outside temp drops to 0F with 30MPH winds, like, you know, the last several months. I should have put in an alarm feature, and will next iteration, for water pipe monitoring. May need to go one-wire network at that point, so I can string sensors along a pipe.

 

Now, to the meat. (without putting all of the code in the visible post.... see the attachment for the whole mess)

 

The one-wire comm is timed using cycle delays. This is not ideal. It is, on the other hand, straightforward. The MSP430 is running at 16MHz, and the basic timing unit for the one wire comm is 1us. Most of the timing is not that critical, so I made no effort to get precision on longer timeframes. As noted already, much of this could (and eventually will) be redone using hardware timers and interrupts. Only the bit timing comes close to needing precision.

 

For those not familiar with the communication scheme, the one wire comm is a bidirectional open collector bus system. The comm line needs a pullup resistor, and the master provides all timing. The line is high when inactive, and all communication timing is synch'd by the master pulling the line low. The nominal bit period is 60us, with the data being valid at 15us. If the line is high at 15us after being pulled low, it is a '1', and if it is still low, it is a '0'. The reason for the bit frame being 60us is that the sensor does not have a precision clock. It is subject to manufacturing spread and variation due to temperature. The functions ow_0(), ow_1(), and ow_read() are the only ones that are really time critical. These handle the low level tasks of a single bit. They are used by ow_cmd() to transmit a byte to the sensor, and ow_read_byte() to read a transmitted byte from the sensor. ow_read_word() is a convenience for 16bit responses from the device.

 

The only other low level comm routine is ow_init(). It provides the init signal to a device on the line, and returns a status (device detected and successfully reset, or not)

 

 

The other things of interest here are the temp conversion from defC to degF, and the production of a base decimal ASCII representation of the final reading.

 

The conversion is from the sensor measurement in degrees centigrade, fixed point with fraction in 1/16ths, to degrees Fahrenheit, with fraction in 1/10ths. Looks like a pain, as the equivalence is F=9/5C+32. Requires multiplication and division, right? And floating point? No way to divide by 5 using bit shifts and integers? Well, actually, not so fast... The conversion requires nothing but bit shift and adding. Here's how:

 

F=5/9C+32=18/10C+32

 

This means that 10F=18C+320 (the joy of algebra..) There goes the division. I want the tenths, anyway. If I want whole degrees, then I deal with having 10F as I need to.

 

Then I note that I don't have degC as a floating point, I have a fixed point value... An integer that is actually 16C.

 

10F=18(16C)/16 +320. Great! I can divide by 16 using bit shifts! And, I can multiply by 18 the same way, with an add!

 

A little more thought, and I get

 

10F=9(16C)/8 +320=((16C)+(1/8)(16C))+320, implemented as

           tenF=(tC+(tC>>3))+320; // convert degC*16 to degF*10

Fast, small, and gives a result that has a max error of 1/10degF relative to sensor value (due to truncation rather than rounding). Good enough for the task. Still better than the error spec of the sensor.

 

Next, conversion to decimal ASCII. A table of powers of ten is suitable for (most) any integer application, and avoids the need for division or modulo. Additionally, the digits are provided left-to-right, which is a convenience. Successive subtraction and counting produce the digits. The numeric value is built into an array initialized with char '0', so no additional char manipulation is needed, unless leading zero blanking is desired. I wanted it, so I put it in. For a 32bit int, I would use a loop. Here, I used two if's, as there can be no more than two leading zeroes. It also brings the leading sign over for negative values. It is not elegant. It is small and fast. This version is built specifically for a) one decimal fraction digit fixed point, and B) writing directly to output, so the decimal point is not stored in the array. Putting the point in the array would shorten code for output, but would require a fix for the data in the array to put the fraction digit in the correct place. Also, the most sig digit is at the high index, and the fraction digit is at index 0. Makes the code a little shorter. Not so good if it will be used as a string.

const int powers[]={1,10,100,1000,0};

void ser_out_word_dec(int w) { // write a decimal value as ascii digits. Last digit is frac...
	// max 3 digits left of dec
	// in the interest of no mul or div, uses loops and sub
    char digits[]={'0','0','0','0',' ',' '}; // digits array.... rev order. Last is sign and space for propagation
	unsigned i=4;

	// neg?
	if (w<0) {
		w=(0-w);   // make positive for remainder of conversion
		digits[4]='-'; // sign
	}
	for (; i-- { // high order to low
		while (w>=powers[i]) {
			w-=powers[i]; digits[i]++; // if non zero digit, inc char
		}
	} // for... digits array now has chars
	// sign prop
	if (digits[3]=='0') { digits[3]=digits[4]; digits[4]=digits[5];
    	if (digits[2]=='0') { digits[2]=digits[3]; digits[3]=digits[4];}
	}

    ser_write(digits[4]);
    ser_write(digits[3]);
    ser_write(digits[2]);
    ser_write(digits[1]);
    ser_write('.');
    ser_write(digits[0]);
}

Ok. Enough already. The cat has been very patient (sleeping on my lap) while I typed this all up. Any questions are welcome. The current version of the project is about twice as many lines as attached here. If I clean it up and finish it, I may add more later. Right now, nothing else of interest in the current version.

main.c

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...