- - - HOME - - - - MICROLAN PROGRAMMING TUTORIALS INDEX - - - - - Other material for programmers

- - - - - - - MICROLAN CENTRAL: More general 1-Wire Information (Less focused on programming)

Delphi: Using Dallas MicroLan (aka 1-wire) DS1820 temperature sensors

This has good information, and a search button at the bottom of the page

Please don't dismiss it because it isn't full of graphics, scripts, cookies, etc!

Click here if you want to know more about the source and format of these pages.


This page shows you the creation of a simple application to read DS1820 (or similar) temperature sensors. (Family codes $10 and $28). At the heart of it is a set of portable routines which easily plug into other applications.

The sourcecode (ds042source.zip) and the finished exe are downloadable in a zip archive. Note that the archive's sourcecode is for the project's finished form. It evolved through the various intermediate versions which you will experience if you work your way through the tutorial. If you just need to have access to the "read chip" routines, you can lift them from the machine readable version of the sourcecode.

However, if you want the best understanding of what's going on, work through the tutorial. You might want try the exe in the zip beforehand, though, to be sure that your TMEX drivers are set up correctly, etc. It will also allow you to get the ini file right for your circumstances. You may need to re-specify the adapter type or location. You will certainly need to re-specify the chip IDs. In the supplied ini file, both chips have been specified with the same ID, because I only had one on my test MicroLan. The program read the same chip twice. You can put different chip IDs in for chip 1 and 2 if you have two chips!

This tutorial was written in August 2006, working with Delphi 2. If you examine the code carefully, you will come across many "little tricks" that make working with Delphi easier. I won't write this as a Delphi beginner's tutorial, though, so watch out for the "good stuff", as I won't always explicitly mention it. However, I will treat you, gentle reader, as a 1- Wire beginner. (Some very minor work was subsequently done with Delphi 4... it "shouldn't" matter... but let me know, if you find otherwise!)

The version of Delphi should not affect whether the exe file runs on your computer. If you are using a different version to compile the sourcecode, I'll be surprised if you have trouble; please let me know, if you do? One issue that has arisen in the past is the placement of the "uses" clauses. (There's one in the main unit, and in the subordinate one.)

The application we are going to build will have two buttons- one to read sensor "0", the other to read sensor "1".

The application will use an ini file to set up the relevant 1-Wire parameters- adapter type, adapter location (which port it is on), and the sensor chips' IDs.

The routines for reading the chips will be in a discrete unit so they can be "plugged into" other programs easily.

Please forgive a little ad from your sponsor before we go on to the tutorial? I wrote the tutorial while doing the programming for FarWatch- my answer to monitoring premises from afar. All you need at the premises to be monitored is an always-on broadband connection and an old PC, Win98 will do. There's nothing else to buy. (Since I first wrote that paragraph, months have elapsed. At 11/09, as I edit this page, I can say that FarWatch has turned into a capable and exciting application.)
Writing good programs is a "chicken and egg" project. You need a clear idea of where you are going, not least because you need to know you can get there. However, it is also good to spend part of your time thinking about "What will the user SEE?", and leaving the rest of the problems to sort themselves out along the way.

Concentrating on the "What will the user see?" part, begin by creating a form with two buttons, two labels. Clicking a button will read a sensor. The label is for displaying the reading. Call the buttons buReadTture0 and buReadTture1, and call the labels laReadTture0 and laReadTture1. (I eschew "temp" as an abbreviation for "temperature" to avoid confusion with "temporary".)

Name the form DS042f1, Save your start as DS042u1.pas, project DS042, in a folder called DS042.

====
Using 1-Wire chips requires some initialization... you need to have certain values available to the routines that you eventually call, in order, in this program, to read a temperature sensor.

Those values are, in this program, going to come from an ini file. To keep things simple, that ini file will be called DS042ini.txt, and will be in the same folder as the .exe.

===
Getting the 1-Wire info....
===


Set up the following, under that name. I'll explain the parts in a minute...
[Adapter]
type=5
port=1

[Chips]
0=firstchip
1=secondchip
We're going to "use" that ini file very crudely, at first, just to be sure it is working. (You may need different numbers for the keys in the "Adapter" section, and you will need to replace "firstchip" and "secondchip". More on that later.)

Put...
sIniFileName:string;
...into the private declarations for the TDS042f1 class, and in the FormCreate procedure, add...
sIniFileName:=ExtractFileDir(application.exename);
sIniFileName:=sIniFileName+'\ds042ini.txt';
... in order to set up the ini file name.

Add...
IniFiles
... to the program's "uses" clause, and add...
dfIniFile:TIniFile;
to the private declarations for the TDS042f1 class, and- for the moment- in the FormCreate procedure, add...
dfIniFile:=TIniFile.create(sIniFileName);
laTture0.caption:=dfIniFile.ReadString('adapter','type','xx');
dfIniFile.free;
This rather crude code goes to the ini file, picks up what's in the Adapter/Type line, and puts it (just to see it works) in the laTture0 label. If there is no Adapter/Type line, 'xx' will be put in the laTture0 label.

Eventually, of course, we will put what we read from the ini file in better places.

First... what if there is no ini file??

First re-name your current DS042ini.txt so that you can get it back later.

Modify the previous code, making it....
procedure TDS042f1.FormCreate(Sender: TObject);
   procedure CreateIniFile;
   begin
   dfIniFile:=TIniFile.create(sIniFileName);
      dfIniFile.WriteString('adapter','type','you must put number here');
   dfIniFile.free;
   end;

begin
DS042f1.caption:='DS042'+ver;

sIniFileName:=ExtractFileDir(application.exename);
sIniFileName:=sIniFileName+'\xxds042ini.txt';

if not fileexists(sIniFileName) then begin
  CreateIniFile;
  end//no ; here
  else begin
   dfIniFile:=TIniFile.create(sIniFileName);
      laTture0.caption:=dfIniFile.ReadString('adapter','type','xx');
   dfIniFile.free;
   end;//else
end;
Next, we're going to create global variable for the adapter type, adapter port, and the chip IDs. The chip ids are going to be in an array, and we're going to have a constant telling us the maximum valid value for a chip array element index....

These variables are as follows, and are to be declared in the private declarations for the TDS042f1 class....
saDMLRomID:array [0..kDMLRomIndexMax] of string[16];
    (*While this example accesses only two chips, it is
      written so that extending it to more is easy.
      saDMLRomID[0] would hold 'A20000000547FB10' if
      the first chip's id were $A20000000547FB10. The
      address is given in the form returned by the
      iButtonViewer (software included in SDK), i.e.
      the right hand byte is the device ID byte.*)
    baDMLRomIDTable:array [0..kDMLRomIndexMax,0..7] of byte;
    (*baDMLRomIDTable[2,0] holds chip 2's first id byte*)
    baDMLRomIDofOne:array [0..7] of byte;
    (*A "temporary" variable, for passing ID of a ROM to
      external routine.
      baDMLRomIDofOne[0] holds a chip's first id byte*)
    sIniFileName:string;
    dfIniFile:TIniFile;
    iDMLPortNum,iDMLAdapterType,iDMLTmpResult:integer;
    (*iDMLTmpResult is a 'scratch' variable, never used for
         long-term storage of any value.*)
    liDMLSHandle:longint;

  sTmp:string;
  iErr:integer;
  //boTmp:boolean;
  bTmp:byte;
=====
Apologies if some other variable declarations crept in there.. I had to go back and re-work this section of this tutorial at one point!

===
Set the constant kDMLRomIndexMax to 1 in the applications's const clause.

Change....
laTture0.caption:=dfIniFile.ReadString('adapter','type','xx');
... to....
sTmp:=dfIniFile.ReadString('adapter','type','xx');
 val(sTmp,iDMLAdapterType,iErr);
 if iErr<>0 then begin
   showmessage('The adapter type must be a number');
   application.terminate;
   end;
(The problems this addresses could be handled more elegantly, but this will do.)

N.B.: At least in Delphi 2, there SOMETIMES seems to be something I don't understand about ini file handling. If you edit and re-save the ini file, and then re-run the application from within the IDE, the applications does not always seem to notice the change you've made to the ini file. If you run the application directly from the .exe with a shortcut, it does seem to notice the edit. You have been warned! (I have wasted many hours working on such things.)

Expand the two sections between the TIniFile.create / TInifile.free to provide for filling iDMLAdapterPortNum, saDMLRomID[0] and saDMLRomID[1]

The heart of those will be clear from the following, I hope, which is all you need to add to the CreateIniFile section.....
dfIniFile.WriteString('adapter','port','you must put number here');
dfIniFile.WriteString('chips','0','you must put 16 hex characters here');
dfIniFile.WriteString('chips','1','you must put 16 hex characters here');
You will assign the value of the adapter port to iDMLAdapterPort, and the two chip ID strings to saDMLRomID[0] and saDMLRomID[1]. There is some error checking you may want to look at in the source code, which comes with this tutorial.

That's it for "Getting the 1-Wire Info". Whew!

===
Next, a little word about the software side of using 1-Wire chips. This will be a mere re-cap of more extensive information elsewhere.

Your program talks to the TMEX API... some software from Dallas that "sits inside" your computer, waiting to be useful. When your program talks to the TMEX API, your program makes the TMEX drivers talk to the 1-Wire chips.

When reading the data sheets, I sometimes lose track of whether we're talking about the conversation between my program and the API, or about the drivers' conversation with the chips. BE alert for that confusion in your own study.

Any program which is going to interact, through the API, with 1-Wire chips works something like....

a) Once and for all, initialize the contents of various variables, e.g. the ones saying where the adapter will be found, what the chip's ID is.

b) Subsequently, repeat as needed....

-Establish a Session Handle. This is the link. If you look closely at the code that follows, you will see that my procedure for dealing with this phase incorporates two TMEX calls, one to TMExtendedStartSession and then one to TMSetUp. They only need calling once.
-Reset things.
-Access a specific chip.
-Interact with that chip.
-Close, for now, your connection to the API.

That's some of the highlights.

===
How we are going to proceed.

One of the joys of Delphi programming is that you can "parcel" things up in discrete units, which makes re-using code easier.

For this project, I am going to establish a unit called PSD016 (Pascal Subroutines, Delphi). To do that...

File | New | Unit

...then Save As....

PSD016eu.pas

("eu" for "external unit.")

Add text until it consists of....
unit PSD016eu;

interface

const version='23 Aug 06';

function ver:string;

implementation

function ver:string;
begin
result:=version;
end;

end.
To the uses clause of DS042, add 'PSD016eu'.

Just for a quick demo, make the OnClick handler for buTture1 be....
laTture1.caption:=psd016eu.ver;
Run the program, click on the Tture1 button. You should get 23 Aug 06 in the Tture1 label.

Note what we have:

In the unit PSD016eu, the function 'ver' is declared in the interface section, and subsequently "explained" in the implementation section.

In DS043, because we have "uses PSD016", we can use the function PSD016.ver.

Cute! Useful!

We're going to put other things in PSD016. Psd016 is, itself, going to use things from another unit.

The first is code to read from a DS1820 temperature sensor. Before we put the real code in, we're going to build the basis for the communications between the calling program and the code in the unit.

In PSD016, just after the first instance of "function ver:string;", add....
procedure DMLGet1820Tture(
      var bTtureHi,bTtureLo,bTKBErr:byte;
      var iDalErr:integer;
      liHandle:longint;
      baRomID:array of byte);

And just before the unit's final "end.", add the following initial test of some aspects of the code. This won't read the chip!....

procedure DMLGet1820Tture(
      var bTtureHi,bTtureLo,bTKBErr:byte;
      var iDalErr:integer;
      liHandle:longint;
      baRomID:array of byte);
begin
bTtureHi:=250;
bTtureLo:=251;
bTKBErr:=0;
iDalErr:=0;
end;//DMLGet1820Tture
Once that's done, over in DS042, make buTture0's handler be....
procedure TDS042f1.buReadTture0Click(Sender: TObject);
var c1,bTmp1,bTmp2,bTmp3:byte;
    iTmp:integer;
begin

bTmp1:=10;//tmp for devel of DMLGet1820...
bTmp2:=10;//tmp for devel of DMLGet1820...
bTmp3:=10;//tmp for devel of DMLGet1820...
iTmp:=10;//tmp for devel of DMLGet1820...


DMLGet1820Tture(bTmp1,bTmp2,bTmp3,iTmp,liDMLSHandle,baDMLRomIDofOne);
laTture0.caption:=inttostr(bTmp1)+' '+
                  inttostr(bTmp2)+' '+
                  inttostr(bTmp3)+' '+
                  inttostr(iTmp);
end;
Run that. Study it to grasp all that you can about what's going on. Not the "var" in the declaration of the procedure... that's why values get passed back in the calling parameters.

=====
In DS042, just after the declaration of saDMLRomID ("sa" for "string array"), we declared baDMLRomID.

To fill baDMLRomID, we need, in DS042's FormCreate, to declare a local variable c1, type byte. Just after the "dfIniFile.free;" near the end of the procedure, add....
for c1:=0 to kDMLRomIndexMax do begin
       sTmp:=saDMLRomID[c1];
         baDMLRomIDTable[c1,7]:=strtoint('$'+copy(sTmp,1,2));
         baDMLRomIDTable[c1,6]:=strtoint('$'+copy(sTmp,3,2));
         baDMLRomIDTable[c1,5]:=strtoint('$'+copy(sTmp,5,2));
         baDMLRomIDTable[c1,4]:=strtoint('$'+copy(sTmp,7,2));
         baDMLRomIDTable[c1,3]:=strtoint('$'+copy(sTmp,9,2));
         baDMLRomIDTable[c1,2]:=strtoint('$'+copy(sTmp,11,2));
         baDMLRomIDTable[c1,1]:=strtoint('$'+copy(sTmp,13,2));
         baDMLRomIDTable[c1,0]:=strtoint('$'+copy(sTmp,15,2));
         end;(*For c1:=...*)
We also need some other things before we can build the "innards" of DMLGet1820Tture. We will put some of them in PSD016 where they will be available for when we next need them.
procedure DMLEstablishSession(iPortNum,iAdapterType:longint;
                              var bTKBErr:byte; var liHandle:longint);
You need to put that in the interface section of PSD016eu, and, in the implementation section....
procedure DMLEstablishSession(iPortNum,iAdapterType:longint;
                              var bTKBErr:byte; var liHandle:longint);
(*On Exit...
  if bTKBErr=0 then all was well, and a valid session handle is
       established in liHandle
  if bTKBErr=1 then TMExtendedStartSession failed, and error
       code from Dallas returned in liHandle
  if bTKBErr=2 then TMSetUp failed, and error
       code from Dallas returned in liHandle*)
var iDMLTmpResult:longint;
begin
bTKBErr:=0;
liHandle:=TMExtendedStartSession(iPortNum,iAdapterType,nil);
if liHandle<0 then bTKBErr:=1;
if bTKBErr=0 then begin
   iDMLTmpResult:=TMSetUp(liHandle);
   if iDMLTmpResult<>1 then begin
      bTKBErr:=2;
      liHandle:=iDMLTmpResult;
      end;(*iDMLTmpResult<>0*)
   end;(*bTKBErr=0*)
end;//DMLEstablishSession
In order to use DMLEstablishSession, we need to add iBTMEXPW to the "uses" clause of DS043 and to create a "uses" clause with it in PSD016eu. The "uses" clause goes just after "interface". We also need to put a copy of IBTMEXPW.PAS, which Dallas supplies, into the project's folder. We also need "liDMLSHandle:longint;", which I "snuck in" along with all the other global variables a while back. liDMLSHandle won't be directly accessed by the underlying code of DMLEstablishSession, but it will be one of the variables we fill when we call DMLEstablishSession.

The application will also need a a global bTmp, type byte.

=====
Once that's done, over in DS042, make buTture0's handler be....
procedure TDS042f1.buReadTture0Click(Sender: TObject);
var c1,bTmp1,bTmp2,bTmp3:byte;
    iTmp:integer;
begin
laTture0.caption:='One moment...';
//Execution of DMLGet1820... does take enough time that you
//  Really Should add this "frill".
application.processmessages;
//without this, you don't see the caption change,
//   (And even with it, you don't always see it!)
//   because
//   (at 24aug06) DMLGet18200... has a bad, Windows hogging,
//   loop as part of WaitABit. Hey! This works.... even though
//   it shouldn't. I'm not proud. If you can show me a better
//   way within Delphi 2, I'll be delighted!
// ---------------------

//Needed over and over...
   DMLEstablishSession(iDMLPortNum,iDMLAdapterType,bTmp,liDMLSHandle);

if bTmp<>0 then begin
    showmessage('DMLEstablishSession reported an error. Try again. '+
        'It may simply be that MicroLan was busy.');
    end// no ; here
  else begin

//First, put the ID of one chip (from table) into array for passing to Get1820...
//The first index of baDMLRomIDTable determines which chip will be read.
//For less flexibility, more speed, variables
//   baDMLRomIDofChip0, baDMLRomIDofChip1, type array[0..7] of byte
//   could be set up and used. This would save having to fill and
//   refill the "scratch" variable baDMLRomIDofOne.
    for c1:=0 to 7 do
    baDMLRomIDofOne[c1]:=baDMLRomIDTable[0,c1];

    DMLGet1820Tture(bTmp1,bTmp2,bTmp3,iTmp,liDMLSHandle,baDMLRomIDofOne);

    laTture0.caption:=inttostr(bTmp1)+' '+
                  inttostr(bTmp2)+' '+
                  inttostr(bTmp3)+' '+
                  inttostr(iTmp);
//At a room temperature, the result of this may be 0  50  0  xx,
//  where xx is a number. (It means nothing, if the third number is 0.
//  Warm the device with your fingers, and the 50 will rise.)
//If third number not a zero, it means there was an error.
end;//else
end;//buReadTture0Click
=====
Whew! See if the program runs now... it should, but, for the first time, you will need a MicroLan adapter attached to your PC, and the adapter port and type will need to be correctly specified in the ini file.

Once it is working, it is time you took a break!

====
Back to the fray!

So far, we have a program that will...

1) Collect some configuration information from an ini file.

2) Set itself up for some 1-Wire activity.

Now we need to add a procedure to do some useful 1-Wire activity, but first we need to attach a 1-Wire temperature sensor, and put it's ID in the ini file. If you only have one sensor put it's ID in for both chip 0 and chip 1. The program can read 1-Wire temperature sensors with IDs that end 10 or 28.

If you've been following along assiduously, we already have a program that "reads" a chip, using a procedure inside PSD016. (Click on the Read Tture0 button, and you should get "250 251 0 0" on the tture1 label. Not the temperature, but what the procedure DMLGet1820Tture currently returns.

Inside PSD016, add the following, just before "implementation....
var    siaDMLRom:array[0..8] of smallint;(*Yes: 9 bytes.
       the last isn't used every time, but I think
       it IS used sometimes.

       A curiosity: The type MUST(?) BE SMALLINT...
       I accidentally had this as byte and got
       GPFaults during TMRom call. (Problem
       stuffing $FF into an element?)*)

       baDMLStateBuffer:array[0..15361] of byte;
      (*15360 might do, but for one byte, I covered
       the possibility that I was misunderstanding
       something in the Dallas docs*)

       liHandle:longint;
... and replace the current DMLGet1820Tture code with the following. I'm sorry it is such a mess... but that is the glory of units... any calling program doesn't know what's in the mess, and as it is cleaned up, all calling programs will benefit.

======Start DMLGet1820Tture code =====
procedure DMLGet1820Tture(
      var bTtureHi,bTtureLo,bTKBErr:byte;
      var iDalErr:integer;
      liHandle:longint;
      baRomID:array of byte);

// Here begins....
// A LONG bit of code!...
// AND BAD if remmed out "application.processmessages"  in WaitAWhile
// not fixed or replaced yet.

//This code "ain't pretty", but I believe that the header is a
//   basis which can be built on. It is about my third attempt...

//The code is suitable for chips with family codes $10 and $28.


var iDMLTmpResult:integer;
    //boSetup:boolean;

(*This DMLGet1820Tture derived from
  DMLGet1820TtureJly06, which had significant changes from
  the older DMLGet1820Tture, even though it started from there.

AT PRESENT (12Jly06), it SHOULD work with ds1820's which are
supplied with 5v non-parasitically. It will even work with
parasitically powered 1820s, sometimes, but to make it work
reliably using only parasitic power, the software is supposed
to provide strong pull up...
which not all(?) adapters can do...*)

(*KEEP THIS AND dmlGet18B20Tture TUTORIAL/DS code IN STEP

THIS (in PSD016) modified from
"THIS" (in DS025) modified 10jly06, but it should still have the
"new at 10jly06" feature that even if tkbErr<>0,
procedure still releases session at end of call.
Calls TMEndSession. (line is remmed.)

ALSO added a TMReset block

Other old (at 7/06) "read Dallas chip" routines probably
  need similar.

In the Dallas "read a temperature" demo code, there
  seems to be a number of places where the code says
  "Couldn't do that first time? Try a few times more"
  In this code, we work through the stages of reading
  a temperature, and if we get a snag at any point,
  we give up. It is up to the calling program to
  re-call this as often as it deems worthwhile.

  N.B. This code does not check that the device it has
  been sent to read is in fact a DS1820 (family code $10,
  or compatible device). That check should be made before
  this routine is called.*)

procedure WaitAWhile;
(*NOT a good idea, on several fronts.. but it works, and
  the program has been constructed so that a better
  solution can be implemented with few side effects.

Error codes may be confused, and non-unique, but working
towards....
nnnn.1110: A CheckSession failure
... otherwise, MSB carries info from last stages of routine,
   LSB info from earlier parts

*)
var lwTmp:cardinal;//used longword in Delphi 7
begin
//Add 'WINDOWS' to the unit's USES clause in order to use "GetTickCount"
//This routine was once in the main unit of an application. Since
//    the time it was moved into a subordinate unit,
//    application.processmessages;
//    has not been possible.
//Originally there was one loop....
//     lwTmp:=GetTickCount+1800;(*lwTmp:=lwTmp+500; here causes a 500ms delay*)
//     repeat
//       application.processmessages;
//     until GetTickCount>lwTmp;
//The delay is now made up of several shorter loops in
//     hopes of not crashing Windows through
//     excessive non-release.
//Not the best thing to do, even the old way, but
//     the best I've found so far to meet the needs
//     of the application!

//Doing 4x400 plus 1x200, total 1800, 1.8 seconds delay
lwTmp:=GetTickCount+400;(*lwTmp:=lwTmp+500; here causes a 500ms delay*)
repeat
until GetTickCount>lwTmp;

lwTmp:=GetTickCount+400;
repeat
until GetTickCount>lwTmp;

lwTmp:=GetTickCount+400;
repeat
until GetTickCount>lwTmp;

lwTmp:=GetTickCount+400;
repeat
until GetTickCount>lwTmp;

lwTmp:=GetTickCount+200;
repeat
until GetTickCount>lwTmp;


end;

function FillBufferNResetNAccess:byte;
var c1,bTmpErrLToFillNAcc:byte;
sChipId:string;
//generalize this for big DML library...
//BEFORE calling it, TMExtendedSession and TMSetup need
//   to have executed okay.
//BEFORE calling, baRomID must hold chip's ID. As long as
//   FillBuff... is inside DMLGet1820Tture this will be okay.
//It takes care of moving Dallas ID spec to buffer inside TMEX driver,
//   resetting the MicroLan (part of TMAccess),
//   and then accessing that chip, i.e. shutting down all the rest.
//   TMAccess does not guarantee that a chip of the id you looked for was
//      present, only that SOME chip was present.
//    See... I think... StrongAccess if you need to KNOW chip present.
//    (Different from Strong Pull Up.)
//Returns 0 if all went well, error code otherwise.
begin  //FillBufferNAccess
  bTmpErrLToFillNAcc:=0;//ERRor-flagLocalTo-Fill...
  for c1:=0 to 7 do siaDMLRom[c1]:=baRomID[c1];
  (*Prepare to pass chip ID to a buffer in TMEX driver.
    It MAY not always be necessary to repeatedly refill
    this buffer, but doing so makes this code more generally
    useful.*)
  try (*You could introduce other try... except... end blocks.
        I only put them in where I needed them to work past
        various errors in my code, errors which I hope are
        now gone!*)
  iDMLTmpResult:=TMRom(liHandle,@baDMLStateBuffer,@siaDMLRom);
  //previous returns 1 when all well.
  except
{  on EGPFault do begin
          bTmpErrLToFillNAcc:=31;
          iDalErr:=0;
          iDMLTmpResult:=1;
          end;}
  end;//except
  if iDMLTmpResult<>1 then begin
    bTmpErrLToFillNAcc:=3;
    iDalErr:=iDMLTmpResult;
    end;

  if bTmpErrLToFillNAcc=0 then begin //x1
  iDMLTmpResult:=TMAccess(liHandle,@baDMLStateBuffer);
  //TMAccess does MicroLan reset prior to shutting down other chips.
  //Returns 1 is all well. (Can be fooled if chip of spec'd ID not on MivroLan)
  if iDMLTmpResult<>1 then begin
    bTmpErrLToFillNAcc:=4;
    iDalErr:=iDMLTmpResult;
    end //no ; here
    else bTmpErrLToFillNAcc:=0;//Turn TMEX "no problem" into TKB "no problem"
  end;//x1

result:=bTmpErrLToFillNAcc;
end;//FillBufferNAccess

begin (*DMLGet1820TtureJly06*)
bTKBErr:=0;
bTtureHi:=0;
bTtureLo:=0;

bTKBErr:=FillBufferNResetNAccess;//rework error codes and how things pass

if bTKBErr=0 (*CSess*) then begin
        iDMLTmpResult:=TMValidSession(liHandle);
(*Check to see that liHandle, the session handle, is still
    valid*)
if iDMLTmpResult<>1 then begin
    bTKBErr:=14;// 0000.1110
    iDalErr:=iDMLTmpResult;
    end;
end;

(*Note "22Jun02": See DD17dU1 for possible enhancing code
 involving invoking strong pull up here.. the other half
 of this idea is flagged below with a ref to 22Jun03

 7/06: Hardware probably NEEDS strong pull up here,
     if DS1820's may be parasitically powered....
 (8/06)...but  they WILL work sometimes, even
     parasitically powered.

 7/06... this note may now be in wrong place*)

{if bTKBErr=0 (*CSess*) then begin
        iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
    bTKBErr:=16+14;//MSB says WHICH CSess, LSB of 1110 says "A CSess failed"
    iDalErr:=iDMLTmpResult;
    end;
  end;
  }

  // HERE IS where "go into strong pull-up after next op" should(?) be (7/06 note)

(*Now send message to chip telling it to take a reading*)
if bTKBErr=0 then begin
  iDMLTmpResult:=TMTouchByte(liHandle,$44);//ConvertT cmnd
  if iDMLTmpResult<0 then begin
    bTKBErr:=5;
    iDalErr:=iDMLTmpResult;(*-1 flags shorted ML*)
    end;
  end;

if bTKBErr=0 then WaitAWhile;
(*The above causes a delay so that the chip can complete
the process of taking a tture reading. There are better
ways to make a delay, and even better, there's a way
to ask the chip if the conversion is completed. Hey! This
is still worth what you paid me for it!

How long is long enough? That depends... It's probably worth
using the iButtonViewer (s/w supplied with SDK) to see
what sort of delays your chip is needing.

There is much talk in the literature about the "need" for
the master to put the ML into a string pull up state
during the conversion.... but the hardware I had (DS9097E+
DS1820) worked, even though I don't think I was able to have
a string pull up from the DS9097E. Go figger.

In a related, I suspect, vein: If in doubt, if possible, avoid
parasitic powering of devices. What's an extra wire for something
that works?! "One-Wire" was already two, with the ground wire.

After conversion, the tture should be in bytes 0 and 1 of
the scratchpad, lsb/msb respectively.*)

if bTKBErr=0 (*CSess*) then begin
        iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
    bTKBErr:=32+14;
    iDalErr:=iDMLTmpResult;
    end;
  end;


//HERE insert "leave strong pull up state" (7/06 note)
(*See note 22Jun03 above re...
See DD017d for the other half of the strong pull up stuff.*)

{reserved for cancel strong pull up...

if bTKBErr=0 then begin
err:=60
  end;

if bTKBErr=0 (*CSess*) then begin
        iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult then begin
    bTKBErr:=62;
    iDalErr:=iDMLTmpResult;
    end;
  end;         }

(*Now we start near the top of the sequence, this time
  to access the tture reading...*)
if bTKBErr=0 then begin
  bTKBErr:=FillBufferNResetNAccess;//need to refine error messaging
  end;

if bTKBErr=0 (*CSess*) then begin
        iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
    bTKBErr:=32+16+14;
    iDalErr:=iDMLTmpResult;
    end;
  end;

if bTKBErr=0 then begin
  iDMLTmpResult:=TMTouchByte(liHandle,$BE);//Send Read Scratchpad cmnd
  if iDMLTmpResult<0 then begin
    bTKBErr:=8;//(7 may still be avail... jumped to 8 from 6)
    iDalErr:=iDMLTmpResult;
    end;
  end;

{No Check Session in DD17d... Would adding one wreck things?
if bTKBErr=0 (*CSess*) then begin
        iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
    bTKBErr:=82;
    iDalErr:=iDMLTmpResult;
    end;
  end;         }

//Start reading 9 bytes from DS1820... 1st holds low byte of TTure
if bTKBErr=0 then begin
  iDMLTmpResult:=TMTouchByte(liHandle,$FF);
  bTtureLo:=iDMLTmpResult and $FF;
  if iDMLTmpResult<0 then begin
    bTKBErr:=8+1;//error codes get hairy if you go for remaining 7 data
    iDalErr:=iDMLTmpResult;
    end;
  end;

if bTKBErr=0 (*CSess*) then begin
        iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
    bTKBErr:=64+14;
    iDalErr:=iDMLTmpResult;
    end;
  end;

//Read 2nd byte of datastream from 1820- high byte of TTure
if bTKBErr=0 then begin
  iDMLTmpResult:=TMTouchByte(liHandle,$FF);
  bTtureHi:=iDMLTmpResult and $FF;
  if iDMLTmpResult<0 then begin
    bTKBErr:=8+2;
    iDalErr:=iDMLTmpResult;
    end;
  end;

//That's the tture data read. For a more robust application,
//read the following 7 bytes, too, as the last holds a CRC
//for testing whether data valid.

{DD17d had no CSess here, based, I believe, on Dallas sample code
 would one hurt? matter?
if bTKBErr=0 (*CSess*) then begin
        iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
    bTKBErr:=102;
    iDalErr:=iDMLTmpResult;
    end;
  end;      }

(*Following is NEW (in DS025) 10Jly06, and if successful needs
  adding to Tut versions, etc*)

(*Even if bTKBErr<>0....
  Reset perhaps not strictly necessary, as any access should START with
     a reset. Waste of cycles? Tidy?*)

{ 12Jly06: IT MIGHT SEEM LIKE A GOOD IDEA TO RESET THE MICROLAN,
   AND THE DEVICES ON IT, READY FOR NEXT READ CYCLE... BUT IF
   YOU DO, ALL SORTS OF PROBLEMS ARISE FOR SUBSEQUENT TMSETUPs.
   TIME NEEDED AFTER TMTOUCHRESET BEFORE A TMSETUP CAN BE ISSUED?
   WHATEVER.... REMOVING THIS TMTOUCHRESET MADE A WHOLE BUNCH
   OF PROBLEMS GO AWAY!!

if boSetup then begin //reset Microlan
iDMLTmpResult:=TMTouchReset(liHandle);//Done. Reset devices on the
         //Microlan so they are ready for whatever comes next.
  if (iDMLTmpResult<>1)and(iDMLTmpResult<>2) then begin
    ReportError('Problem in DMLGet1820TtureJly06 10jly06b.');
    bTKBErr:=64+bTKBErr;
    iDalErr:=iDMLTmpResult;
    end;
end;// of "if boSetup..."
}

{EVEN if bTKBErr<>0 then.... <-mod of 10jly06... and what follows expanded...}
iDMLTmpResult:=TMEndSession(liHandle);
  if iDMLTmpResult<>1 then begin
    //replace somehow ReportError('Problem in DMLGet1820TtureJly06 10jly06a.');
    bTKBErr:=128+bTkbErr;//64 bit may already be set.
    //needed???boErrorReported:=true;
    iDalErr:=iDMLTmpResult;
    end;

(*I suppose that in some circumstances, leaving the
  session running continuously might work. I opted
  for the simpler and more general answer: Start it
  just before each use, shut it down as soon as not
  needed immediately. There is an entry in the TMEX
  help file suggesting that leaving a session
  running is a bad idea.

  Puzzle....
     Is there a way to do TMExtendedStartSession
     and TMSetUp just once, and then access the API
     multiple times, each with an TMEndSession??
     One attempt at that failed miserably, but I
     think I am doing just that in some other program.
     Maybe something that only works if only one
     running program is accessing the API?*)

end;(*DMLGet1820Tture*)
======End DMLGet1820Tture code ====

Now the program should "work" better.... when you click on the Read Tture0 button, you get something like....

0 49 0 xx

... where xx is a number (quite large, often).

If the third number is not a zero, then something's wrong.

The first number may not be zero, if the sensor is quite hot.

Warm the sensor with your fingers and the next time you click the button, the 50 (or whatever you had) will go up. Let it cool, click again, and the number will go down.

"Working!"... but hardly units we know and love, so.....

====
Getting temperatures in degrees Celsius:

When you read a Dallas 1-Wire temperature sensing chip, two bytes of data are returned. Converting them into units we use every day is a pain. The rules for converting the values are different depending on which sort of temperature sensor you were working with. You know which sensor type you have from the last byte of the chip ID, which is in baDMLRomIDofOne[0].

The necessary code (function rRawToC18B20 and function rRawToC) is in PSD016, and the code that is executed when you click on the ReadTture0 (or ReadTture1) is in the sourcecode for this tutorial, as distributed. Examine that code and the comments there if you want the details.

Let me confess to a flaw in the application as it stands. It isn't too hard to fix.. but I leave that as "an exercise for the student". After you click the "Celsius" or "Fahrenheit" radio buttons, any subsequent readings will be returned in the units you've chosen. The application really should change the display as soon as you make a units choice, converting the on-display prior readings to the units selected. Sorry about that untidiness.

====
That takes care of the essentials.

Some frills:

In the source code supplied with this, there is a function (CtoF) to turn a Celsius number into a Fahrenheit number. A pair of radio buttons (on a panel for tidiness' sake) control whether output is in Celsius of Fahrenheit. No OnClickRadioButton code is needed.... There's simply a test of rbFahrenheit.checked, and if so, the temperature is converted.

From the beginning, this application was designed to report the state of two sensors. So far we have only created the code for the ReadTture0 button.

The code for the ReadTture1 button could be created simply by a quick copy/ paste, with a few tweaks of things like "laTture0", which would become ""laTture1", but a more complex approach has advantages.

In DS043, we're going to write a combined "FetchTture" routine with a parameter to tell the routine which sensor to read, label to use, etc. the OnClick handlers for ReadTture0 and Tture1 then become mere calls of FetchTture. The disadvantage of the "simpler" scheme is that it is easy for the two blocks of code to get out of step with one another. Furthermore, in some projects, the blocks of code needing duplicating if you don't use a trick like this can be very large.

I'm not saying more about that here... you can see it in the code, if you are interested.


For more on MicroLans and why they're cool, see... My guide to MicroLan


   Search this site or the web        powered by FreeFind
 
  Site search Web search
Site Map    What's New    Search


Click here if you're feeling kind! (Promotes my site via "Top100Borland")
Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
--Click here to visit editor's freeware, shareware page.--

Link to Tutorials main page

Info on how to contact this page's editor, Tom Boyd.


Valid HTML 4.01 Transitional Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....