Differences

This shows you the differences between two versions of the page.

Link to this comparison view

atmel:ds18b20-2313 [2009/11/27 17:53] (current)
Line 1: Line 1:
 +====== Temperature logger ======
  
 +Monitor up to 7x DS18b20 1-wire devices using the [[http://​www.atmel.com/​dyn/​resources/​prod_documents/​doc0839.pdf AT90S2313]] AVR microcontroller. ​ Each device will report its unique ROMID along with the temperature to the rs232 port whenever a change in temperature is detected.
 +
 +{{:​atmel:​cimg1833.jpg|2313 temperature board}}
 +
 +Two DS18b20 shown, one directly attached to the PCB via a 3pin socket others can be connect via additional pins.
 +
 +The current draw from this device is 25mA.  This is too much to drive from the RS232 port however a USB port can drive up to 500mA at 5v.  Perfect.
 +
 +How to save current:  ​
 +  * if we went for an interrupt driven approach rather then a tight loop we could idle down the chip in the meantime. ​ However we are tight on code space. ​ At IDLE the circuit still draws 17mA - so we may save up to 8mA using an interrupt driven approach.
 +  * Using a lower frequency chip say 4Mhz.
 +
 +Alas its already built now.
 +
 +<​code>​
 +2313 Temperature monitor
 +Found 2 1-wire devices
 +28A83FF6010000DB28 22
 +28EA31F60100006228 22
 +28A83FF6010000DB28 23
 +28A83FF6010000DB28 22
 +</​code>​
 +
 +Reference:
 +  * http://​pdfserv.maxim-ic.com/​en/​an/​AN162.pdf - 1-wire microcontroller interfacing
 +  * http://​pdfserv.maxim-ic.com/​en/​an/​AN162.pdf - DS18B20 1-wire digital thermometer
 +
 +{{:​atmel:​2313temp.zip|}}
 +
 +<code vb>
 +'​********************************************************************
 +'* 2313temp.bas
 +'*
 +'* Measure temperature using up to 4 1-wire 18b20 or 18s20 devices
 +'* BASCOM
 +'​********************************************************************
 +
 +$regfile = "​2313def.dat"​
 +$crystal = 10000000
 +$baud = 9600
 +
 +Config 1wire = Pd.4
 +
 +' DS18x20 ROM ID
 +Const Ds18s20_id = &H10
 +Const Ds18b20_id = &H28
 +
 +' COMMANDS
 +Const Ds18x20_convert_t = &H44
 +Const Ds18x20_read = &HBE
 +
 +Declare Sub Convallt ​                                       ' Convert T on ALL sensors
 +Declare Sub Meas_to_cel(id As Byte , Offset As Byte)
 +Declare Sub Monitor
 +Declare Sub Disp_temp(cnt As Byte , Offset As Byte)
 +
 +' 1-wire devices
 +Const Max1wire = 7
 +' Up to 7 each having an 8 byte ROMID
 +Dim Dsid(56) As Byte                                        ' Dallas ID 64bit inc. CRC
 +Dim Dsvalue(max1wire) As Byte                               '​ Value of each sensor
 +Dim Dssign As Byte                                          ' If max1wire > 7 make this a word
 +Dim Cnt1wire As Byte                                        ' Number of 1-wire devices found
 +
 +Dim I As Byte
 +Dim J As Byte
 +Dim K As Byte
 +Dim Meas As Word
 +Dim Sc(9) As Byte                                           '​ 1-wire scratch pad
 +'Sc(1) '​Temperature LSB
 +'Sc(2) '​Temperature MSB
 +'Sc(3) '​TH/​user byte 1 also SRAM
 +'Sc(4) '​TL/​user byte 2 also SRAM
 +'Sc(5) '​config ​ also SRAM x R1 R0 1 1 1 1 1 - the r1 r0 are config for resolution - write FF to byte for 12 bit
 +'Sc(6) 'res
 +'Sc(7) 'res
 +'Sc(8) 'res
 +'Sc(9) '8 CRC
 +
 +'​************* MAIN **************
 +Print "2313 Temperature monitor"​
 +
 +' Gather ROM ID for all 1-wire devices
 +Cnt1wire = 1wirecount()
 +Print "Found " ; Cnt1wire ; " 1-wire devices"​
 +If Cnt1wire = 0 Then
 +  Idle
 +End If
 +
 +If Cnt1wire > Max1wire Then
 +  Cnt1wire = Max1wire
 +End If
 +
 +I = 1
 +Dsid(i) = 1wsearchfirst()
 +For J = 1 To Cnt1wire
 +  I = I + 8
 +  Dsid(i) = 1wsearchnext()
 +Next
 +
 +Do
 +  Convallt
 +  Waitms 750
 +  Monitor
 +Loop
 +
 +End
 +
 +Sub Monitor
 +  J = 1
 +  For I = 1 To Cnt1wire
 +      1wverify Dsid(j) ​                                     '​Issues the "Match ROM "
 +      If Err = 0 Then
 +        1wwrite Ds18x20_read
 +        Sc(1) = 1wread(9)
 +        If Sc(9) = Crc8(sc(1) , 8) Then
 +          Call Disp_temp(i , J)
 +        End If
 +      End If
 +
 +    J = J + 8
 +  Next
 +End Sub
 +
 +
 +'Makes the Dallas "​Convert T" command on the 1w-bus configured in "​Config 1wire = Portb. "
 +'WAIT 200-750 ms after issued, internal conversion time for the sensor
 +'SKIPS ROM - so it makes the conversion on ALL sensors on the bus simultaniously
 +'When leaving this sub, NO sensor is selected, but ALL sensors has the actual
 +'​temperature in their scratchpad ( within 750 ms )
 +
 +Sub Convallt
 + ​1wreset ​                                                   ' reset the bus
 + ​1wwrite &​HCC ​                                              '​ skip rom
 + ​1wwrite Ds18x20_convert_t
 +End Sub
 +
 +
 +' Store WHOLE degree'​s only
 +' Display if the value has changed since last polled.
 +Sub Disp_temp(cnt As Byte , Offset As Byte)
 +  Meas = 0
 +  Meas = Makeint(sc(1) , Sc(2))
 +
 +  ' 18S20 is only 9bit upscale to 12bit
 +' ​ If Dsid(offset) = Ds18s20_id Then
 +' ​   Meas = Meas And &HFFFE
 +' ​   Shift Meas , Left , 3
 +' ​ End If
 +
 +  If Meas.15 = 1 Then                                       '​ if meas & &H8000 then
 +    Set Dssign.cnt
 +    ' convert to +ve, (two's complement)++
 +    Meas = Meas Xor &HFFFF
 +    Incr Meas
 +  Else
 +    Reset Dssign.cnt
 +  End If
 +
 +  Shift Meas , Right , 4
 +  ' If this value different from the stored value ?
 +  If Dsvalue(cnt) <> Meas Then
 +    ' Dump ROMID of 1-wire device followed by temperature
 +    K = Offset + 8
 +    Do
 +      Print Hex(dsid(offset));​
 +      Incr Offset
 +    Loop Until Offset > K
 +    Print " ";
 +    If Dssign.cnt = 1 Then
 +      Print "​-";​
 +    End If
 +    Print Meas
 +    Dsvalue(cnt) = Meas
 +  End If
 +
 +End Sub
 +</​code>​
 +
 +To read the device from a linux box we can use the following snippet of perl.  Now that we have a data value in a file we can use this value to feed my [[:​rrdtools]] logging system. ​ Why use an intermediate file?  The temperature sensor only sends a value when it detects a change however the logging software wants a value on a periodic basis ;so it can poll this file.
 +
 +I guess I could have changed the firmware to send a value every (x) mins, however if you want different polling frequencies then you now need to change the firmware to suit, which is more difficult than adjusting your CRON that reads this file!.
 +
 +<code perl>
 +#​!/​usr/​bin/​perl
 +use Device::​SerialPort;​
 +
 +# Map 1-wire ROMID to a location
 +%rommap=(
 +      "​28EA31F60100006228"​ => "​inside",​
 +      "​28A83FF6010000DB28"​ => "​outside"​
 +);
 +
 +# setup SERIAL port
 +my $port = Device::​SerialPort->​new("/​dev/​ttyS0"​) || die "​Can'​t open serial port" ​                ;
 +$port->​databits(8);​
 +$port->​baudrate(9600);​
 +$port->​parity("​none"​);​
 +$port->​stopbits(1);​
 +$port->​handshake("​none"​);​
 +$port->​write_settings || die "no settings";​
 +
 +# Read from serial device
 +my $buffer = "";​
 +while(1) {
 +    while ($string_in = $port->​input) {
 +        $buffer .= $string_in;
 +        while ($buffer =~ m/\n/) {
 +                ($buffer, $rest) = split('​\n',​$string_in);​
 +                process_temp($buffer);​
 +                $buffer = $rest;
 +        }
 +     }
 +     ​sleep(1);​
 +}
 +exit;
 +
 +sub process_temp() {
 +  my ($output) = @_;
 +  my ($romid, $temp) = split('​ ', $output, 2);
 +
 +  my $location = $rommap{$romid};​
 +  if ($location eq ""​) {
 +     ​$location = $romid;
 +  }
 +  # We update the temperature when a new value is received.
 +  # This file will be polled and logged by another
 +  $temp =~ s/​[\r\n]//​g;​
 +  $file = "/​var/​run/​1wire/​$location";​
 +  open(FH,">​$file"​) || die "​cannot open $file for write";​
 +  print FH "​$temp\n";​
 +  close(FH);
 +}
 +</​code>​
 +
 +We run this PERL code in a cron job that poll every 5 mins and transmits the value to the RRD server.
 +<​code>​
 +*/5 * * * * /​usr/​local/​bin/​xmit_temp.pl >/​dev/​null
 +</​code>​
 +
 +<code perl>
 +#​!/​usr/​bin/​perl
 +#
 +# rrd_hddtemp.pl
 +
 +use IO::Socket;
 +use File::​Basename;​
 +
 +# Remote RRD server and port
 +my $host = "​elmo";​
 +my $port = 13900;
 +
 +my $socket = IO::​Socket::​INET->​new(PeerAddr=>​ $host,
 +                                PeerPort=>​ $port,
 +                                Proto=> '​tcp',​
 +                                Type=> SOCK_STREAM)
 +                                or die "​Can'​t talk to $host at $port";​
 +@files = </​var/​run/​1wire/​*>;​
 +foreach $file (@files) {
 +   &​ProcessHDD($socket,​ $file);
 +}
 +
 +close $socket;
 +
 +sub ProcessHDD
 +{
 +    my($socket,​$file) = @_;
 +    open(FH,​$file);​
 +    $temp = <FH>;
 +    close FH;
 +
 +    $loc = basename($file);​
 +    $temp =~ s/[\n ]//g;
 +#    print "$loc : $temp degrees C\n";
 +
 +    print $socket "​update $loc.rrd -t temp N:​$temp\n"​ ;
 +    $answer = <​$socket>;​
 +    print $answer;
 +}
 +</​code>​
 +
 +{{tag>​bascom programming ds18b20 avr}}