Using Liberty Basic to Interface a PC (Windows) with a TM #125 Temperature Module

copyright, Peter H Anderson, Baltimore, MD, Feb, '05


(Feb 6, '05).

Introduction

This application note discusses Serial RS232 communication between a PC running Windows and a TM #125 temperature module. In fact, this could be extended to virtually any of my temperature and control modules, or indeed to any processor. Typical applications include measurement and perhaps in the case of a thermostat or alarm, acting on the measurement, performing calculations and logging data.

I opted to develop this material using Liberty Basic for Windows. It is relatively inexpensive and appears exceptionally well supported by many dedicated people. It appears well structured and is close enough to C / C++ for me to at least make a good stab at writing simple programs. No knowledge of Windows is required.

A demo copy of Liberty Basic is free but there is an annoying time delay each time you desire to compile. There is a $29 version which removes this delay and a $49 version which provides for making executables. Factors in selecting Liberty Basic were ease of use in implementing the serial communications, it widely used and well supported and relatively inexpensive.

I will confess that my knowledge of programming in Basic is limited and it seems as if each day I make some new discovery. However, if I wait to post material I am proud to post, I fear nothing will ever be posted. So, please take this as a possible starting point for your own creative being.

Please note that Liberty Basic is one approach. I make no claim that it is better or worse than any other approach. There are simply so many platforms that I decided to simply implement some samples using Liberty. I decided some time ago that I could not be good at all things and decided to strive to be good in developing the interfacing processors. Programming on the PC side is not one of my peaks of excellence.

The following Liberty Basic programs were tested using Windows 98SE.


Format of the TM #125 Measurement Data

Recall that when the TM #125 unit receives any character, it performs a temperature measurement on each Dallas 1-W sensor found on the single run. A sample;

   00 21.43 106A0B4C 00080003
   01 21.37 109DD748 00080085
   02 21.87 225BFC06 000000CD
   >
where the first field is a sequentially assigned number, the second is the temperature and the third and fourth are the Dallas assigned serial number.

Note that the prompt > may be interpretted as the end of the sequence.


Program TM125_1.Bas.

This program initiates a sequence of temperature by the TM #125 each minute. The results are fetched and displayed and also logged to a file. When the user enters any character in the text window, the measurement is halted and the content of the log file is displayed to the window text box.

In the following, I first attempt to discuss each portion of the program. This is followed by the program in its entirety.

Text Box

I found that the only way I could interrupt the process to stop logging and display the logged results was to use the INKEY$ function which required a text box. My work is adapted from an example in the Liberty Basic archive.

    ' define a box
    nomainwin

    WindowWidth = 400
    WindowHeight = 300
    texteditor #window.te, 0, 0, 391, 254     'The handle for our texteditor is #window.te
    graphicbox #window.gb, 800, 1, 10, 10
    open "kb" for window as #window           'The handle for our window is #window
    print #window.gb, "when characterInput [getChar]"   'When the user presses a key go to [getChar]
    print #window, "trapclose [quit]"    'When the user closes our terminal window, go to [quit]
    print #window.te, "!autoresize";   'Tell the texteditor to resize with the terminal window
    print #window, "font courier_new 9";

Note particularly that when a character in input, the program branches to [getChar].

Data is then written to the text box;

    print #window.te, DataLine$ ' display the string

The character input condition is checked;

    print #window.gb, "setfocus" ' I am a bit confused with this
    scan ' used for check if activity from the keyboard

When a character has been input, the program branches to [getChar] where the character is read and the appropriate action is taken. Note that this could any number of different actions based on the key input. In my case, I directed the program to [display] when any key was input.

[getChar]

    'Whenever the user presses a key, we go here to process it.
    c$ = Inkey$
    ' print #window.te, "at getChar"
    goto [display]

Com Port

The COM Port is opened;

     open "com2:9600,n,8,1, cs0, ds0" for random as #commhandle

I spent a considerable amount of time trying to deal with a COM port error such as the COM port already being open. I don't think I was successful.

Reading from the COM port is a matter of determining if there are characters in the buffer and if so, fetching each character in turn;

    if lof(#commhandle) <>0 then
       c$ = input$(#commhandle, 1)
Writing to the COM port is simply;
    print #commhandle, "!"  ' send any character
Note that with the TM #125, any character initiates a measurement sequence.

Timing.

Liberty Basic permits one to read the time and date off the system clock and I used this too implement the periodic measurements and also to implement a pause.

Commands include time$("milliseconds") and time$("seconds"). The time functions return to number of ms or secs which have elapsed since midnight. Thus, a timeout is a matter of adding the desired interval to a base and continually reading the current time to determine if the current time is greater than the timeout. However, if in adding the interval, the timeout is greater than the number of ms (86,400,000) or secs (8,6400) in a day, the timeout will never be reached. If spent quite a bit of time fiddling with this and ultimately settled on using the date$("days") function as well. Thus, each base time consists of a time and day. If, in adding the interval, the timeout exceeds the number of ms or seconds in a day, the timeout time is adjusted and the timeout date is the next day.

[again]
    TimeOutTime = TimeOutTime + 60 ' every minute

    if (TimeOutTime >= 86400000) then
        TimeOutDate = TimeOutDate + 1
        TimeOutTime = TimeOutTime - 86400000
    end if
    ...
    ...
The program then does its task and then loops until both the current time and current date are greater than the time outs.
    do
        print #window.gb, "setfocus"
        scan ' used for check if activity from the keyboard
        CurrentDate = date$("days")
        CurrentTime = time$("seconds")
    loop until (CurrentTime >= TimeOutTime) AND (CurrentDate >= TimeOutDate)

    goto [again]
Function GetLine$

This function waits up to three seconds to fetch a string from the Com port. If this timeout should occur, the StatusFlag is set to -30000 and returned by reference to the calling process. If the fetched character is the prompt '>', this is returned and the StatusFlag is set to the number of characters in the string. In this case, one. If a fetched character is ASCII 10 (line feed), it is ignored. If it is ASCII 13 (CR), the string is returned with the Status Flag equal to the number of characters. If the fetched character is any other character, it appended to the DataLine$ and the number of characters is incremented.

function GetLine$(ByRef StatusFlag)

    TimeOutTime = time$("milliseconds")
    TimeOutDate = date$("days")

    TimeOutTime = TimeOutTime + 3000 ' three seconds

    if (TimeOutTime >= 86400000) then
        TimeOutDate = TimeOutDate + 1
        TimeOutTime = TimeOutTime - 86400000
    end if


     DataLine$ = ""
     StatusFlag = 0
     NumChars = 0

     x=0
     while x=0
        if lof(#commhandle) <>0 then
           c$ = input$(#commhandle, 1)
           select case
              case  c$ = ">"
                 DataLine$ = c$
                 StatusFlag = 1 ' prompt
                 exit while

              case c$ = chr$(13)
                 StatusFlag = NumChars
                 exit while

              case c$ = chr$(10)    ' ignore it

              case else
                 DataLine$ = DataLine$ + c$
                 NumChars = NumChars + 1

            end select
        else
           CurrentDate = date$("days")
           CurrentTime = time$("milliseconds")
           If (CurrentTime >= TimeOutTime) AND (CurrentDate >= TimeOutDate) Then
              StatusFlag = -30000
              exit while
           end if
        end if
     wend
     GetLine$ = DataLine$
End Function
Writing and Reading from a File.

At the beginning of the program, a file is opened for writing and immediately closed. This either creates the file, in my case "d:\data.txt", or if it already exists, it resets it. This is, it deletes whatever is in the file.

    open "d:\data.txt" for output as #log ' open the file
    close #log
Data is written to the file by opening the file for appending, the data is immediately written and the file is then closed.
    open "d:\data.txt" for append as #log
    print #log, DataLine$       ' write to file
    close #log
The reason for opening the file for appending, quickly writing the data and then closing it to minimize the probability of losing the file is windows should freeze, requiring a reboot or power is lost to the PC. If the file is open, all data is lost.

This might be improved further by copying the current file to a backup file, prior to opening it for appending. Thus, at the worst, one will have the backup file.

Data is read by opening the file for reading and continually testing if there is more lines in the file and if so, reading them.

    open "d:\data.txt" for input as #log
    while eof(#log) = 0
       line input #log, datain$
       print #window.te, datain$
    wend


The Entire Program.


' TM125_1.Bas (Liberty Basic)
'
' Illustrates an interface with temperature module TM #125
'
' Opens a text box.  Opens a logging file d:\data.txt
'
' Every minute sends a character to the TM #125, causing the TM #125 to perform
' temperature measurements on each of the Dallas temperature devices on the single run.
' This program fetches each string from the TM #125 using function GetLine$.
'
' The string is then displayed on the terminal.  In addition, the file is briefly opened, the
' string is written to the file and the file is closed.
'
' This continues indefinitely, until any character is entered from the terminal.  The file is
' then opened and displayed to the terminal.
'
' Illustrates developing a text box, and the use of scan to read a character from the keyboard.
'
' Useful for developing data logger applications.
'
' Uses Com2, 9600 baud.
'
' copyright, Peter H Anderson, Baltimore, MD, Feb, '05


    Com = 16384     ' size of buffer

    ' define a box
    nomainwin

    WindowWidth = 400
    WindowHeight = 300
    texteditor #window.te, 0, 0, 391, 254     'The handle for our texteditor is #window.te
    graphicbox #window.gb, 800, 1, 10, 10
    open "kb" for window as #window           'The handle for our window is #window
    print #window.gb, "when characterInput [getChar]"   'When the user presses a key go to [getChar]
    print #window, "trapclose [quit]"    'When the user closes our terminal window, go to [quit]
    print #window.te, "!autoresize";   'Tell the texteditor to resize with the terminal window
    print #window, "font courier_new 9";

[top]
    oncomerror [closecomm]  ' not sure if this works
    open "com2:9600,n,8,1, cs0, ds0" for random as #commhandle

    call pause 500  ' wait for unit to settle

    open "d:\data.txt" for output as #log ' open the file
    close #log

    TimeOutTime = time$("seconds")
    TimeOutDate = date$("days")

[again]

    TimeOutTime = TimeOutTime + 60 ' every minute

    if (TimeOutTime >= 86400000) then
        TimeOutDate = TimeOutDate + 1
        TimeOutTime = TimeOutTime - 86400000
    end if

    print #commhandle, "!"  ' send any character

    x = 0
    while x = 0       ' loop until either a string is received or timeout

       DataLine$ = GetLine$(NumChars) ' NuChars is passed by reference

       select case

          case NumChars = -30000
              print #window.te, "timeout"
              exit while

          case (NumChars = 1)
              print #window.te, DataLine$
              exit while

          case (NumChars > 2)        ' probably a valid string
              print DataLine$

              open "d:\data.txt" for append as #log
              print #window.te, DataLine$ ' display the string
              print #log, DataLine$       ' and write it to the file
              close #log

          case else
              print "Unknown error"   ' probably an overkill
              exit while

        end select

    wend
    do
        print #window.gb, "setfocus"
        scan ' used for check if activity from the keyboard
        CurrentDate = date$("days")
        CurrentTime = time$("seconds")
    loop until (CurrentTime >= TimeOutTime) AND (CurrentDate >= TimeOutDate)

    goto [again]

[display]
    ' now open the file display the data
    open "d:\data.txt" for input as #log

    while eof(#log) = 0
       line input #log, datain$
       print #window.te, datain$
    wend

    close #log

[Done]
    Goto [Done]

[getChar]

    'Whenever the user presses a key, we go here to process it.
    c$ = Inkey$
    ' print #window.te, "at getChar"
    goto [display]

[closecomm]
     print "Error code: ";ComErrorNumber
     close #commhandle
     goto [top]

function GetLine$(ByRef StatusFlag)

    TimeOutTime = time$("milliseconds")
    TimeOutDate = date$("days")

    TimeOutTime = TimeOutTime + 3000 ' three seconds

    if (TimeOutTime >= 86400000) then
        TimeOutDate = TimeOutDate + 1
        TimeOutTime = TimeOutTime - 86400000
    end if


     DataLine$ = ""
     StatusFlag = 0
     NumChars = 0

     x=0
     while x=0
        if lof(#commhandle) <>0 then
           c$ = input$(#commhandle, 1)
           select case
              case  c$ = ">"
                 DataLine$ = c$
                 StatusFlag = 1 ' prompt
                 exit while

              case c$ = chr$(13)
                 StatusFlag = NumChars
                 exit while

              case c$ = chr$(10)    ' ignore it

              case else
                 DataLine$ = DataLine$ + c$
                 NumChars = NumChars + 1

            end select
        else
           CurrentDate = date$("days")
           CurrentTime = time$("milliseconds")
           If (CurrentTime >= TimeOutTime) AND (CurrentDate >= TimeOutDate) Then
              StatusFlag = -30000
              exit while
           end if
        end if
     wend
     GetLine$ = DataLine$
End Function

sub pause mil
    tcurrent = time$("milliseconds")
    timeout = tcurrent + mil
    if timeout > 86400000 then  ' roll over at midnight
       timeout = timeout - 86400000
       do
           tcurrent = time$("milliseconds")
       loop until (86400000 - tcurrent) > 1000000
    end if

    do
       tcurrent = time$("milliseconds")
    loop until (tcurrent >= timeout)

    end sub

    end


Program TM125_2.Bas

This program extends the above to illustrate fetching the date and time off the system in the form of "mm/dd/yy" and "hh:mm:ss". It also illustrates how to extract a field from a string and convert the field to a number. This might be used to determine if a temperature is in an alarm state. Function ExtractField

With the TM #125, the format of each measurement from the TM #125 is of the form;

00 24.25 106A0B4C 00080003
where the fields are a sequential number, the temperature and the unique Dallas serial number (two fields). If it is desired to extract only the temperature;
     Delimiter$ = " "
     TCelcius$ = ExtractField$(DataLine$, Delimiter$, 2)
The function ExtractString searches for the second occurrence of the delimiter " " and returns the field immediately to the left of this space. This, in this example, Var$ is "00 24.25 106A0B4C 00080003". The index of the first space is found and the string is then cut such that Left$ is "00" and Var$ is "24.25 106A0B4C 00080003". The index of the next space is found and the string is again cut such that Left$ is "24.25" and Var$ is "106A0B4C 00080003". Left$ is then the extracted field which is passed by to the calling procedure.

Strings are passed by reference to a function, that is, the address of the beginning of the variable is actually passed. Thus, as Var$ is modified in the function ExtractField$, variable DataLine$ is also modified. In this program, this was not important as DataLine$ was never again used. Also, variable Delimiter$ must be declared as a variable as one cannot pass the address of the character space.

function ExtractField$(Var$, Delimiter$, NumField)

    for Num = 1 to NumField
       Index = FindDelimiter(Var$, Delimiter$)
       LeftStr$ = Left$(Var$, Index - 1) ' everything to the left of the space
       Var$ = Mid$(Var$, Index+1)        ' to the right of the space
       If Num = NumField Then
          exit for
       End If
    next

    ExtractField$ = LeftStr$

end function

Note that TCelcius$ is a string. If one desires to monitor for an alarm condition, this must be converted to a number using the Val function.
    TCelcius$ = ExtractField$(DataLine$, Delimiter$, 2)
    SerNum$ = ExtractField$(DataLine$, Delimiter$, 3)

    TCelcius = Val(TCelcius$)
    print #window.te, DevNum;  "  "; TCelcius

    if (SerNum$ = "109DD748" AND TCelcius > 25.0) Then
        print #window.te, SerNum$; " is at High Alarm"
    end if

    if (SerNum$ = "109DD748" AND TCelcius < 23.0) Then
        print #window.te, SerNum$; " is at Low Alarm"
    end if

Timing.

Program TM125_2.Bas uses a somewhat different technique for periodically performing a temperature measurement sequence in that it reads the time from the system clock in "hh:mm:ss" format, extracts the minutes field and converts it to a number. Clearly this technique will only work for an interval of minutes in the range of 1 to 59, but it may be reworked to each hour or every four hours.


    T$ = time$()
    Delimiter$ = ":"
    TimeoutMinute = Val(ExtractField$(T$, Delimiter$, 2)) ' extract the minutes field
The interval is then added and adjusted if the result is greater than or equal to sixty.
    TimeoutMinute = TimeoutMinute + 1
    If TimeoutMinute >= 60 Then
        TimeoutMinute = TimeoutMinute - 60
    End If
Later in the program, the clock is continually read, the minute field extracted and converted to a number CurrentMinute which is compared with TimeoutMinute.
    do
        T$ = time$()
        Delimiter$ = ":"
        CurrentMinute = Val(ExtractField$(T$, Delimiter$, 2))
    loop until (CurrentMinute = TimeoutMinute)
The Entire Program

' TM125_2.Bas (Liberty Basic)
'
' Illustrates an interface with temperature module TM #125
'
' Initiates a temperature measurement sequence each minute and displays the date and
' time along with the device number, temperature and serial number to the terminal.
'
' The temperature on one of the temperature sensors is evaluated as to whether it above
' or below a threshold.
'
' Uses Com2, 9600 baud.
'
' copyright, Peter H Anderson, Baltimore, MD, Feb, '05


    Com = 16384     ' size of buffer

    ' define a box
    nomainwin

    WindowWidth = 400
    WindowHeight = 300
    texteditor #window.te, 0, 0, 391, 254     'The handle for our texteditor is #window.te
    graphicbox #window.gb, 800, 1, 10, 10
    open "kb" for window as #window           'The handle for our window is #window
    print #window.gb, "when characterInput [getChar]"   'When the user presses a key go to [getChar]
    print #window, "trapclose [quit]"    'When the user closes our terminal window, go to [quit]
    print #window.te, "!autoresize";   'Tell the texteditor to resize with the terminal window
    print #window, "font courier_new 9";

[top]
    oncomerror [closecomm]  ' not sure if this works
    open "com2:9600,n,8,1, cs0, ds0" for random as #commhandle

    call pause 500  ' wait for unit to settle

    open "d:\data.txt" for output as #log ' open the file
    close #log

    T$ = time$()
    Delimiter$ = ":"
    TimeoutMinute = Val(ExtractField$(T$, Delimiter$, 2))

[again]
    TimeoutMinute = TimeoutMinute + 1
    If TimeoutMinute >= 60 Then
        TimeoutMinute = TimeoutMinute - 60
    End If

    print #commhandle, "!"  ' send any character

    x = 0
    while x = 0       ' loop until either a string is received or timeout

       DataLine$ = GetLine$(NumChars) ' NuChars is passed by reference

       select case

          case NumChars = -30000
              print #window.te, "timeout"
              exit while

          case (NumChars = 1)
              print #window.te, DataLine$
              exit while

          case (NumChars > 2)        ' probably a valid string
              d$ = date$("mm/dd/yy")
              t$ = time$()

              open "d:\data.txt" for append as #log
              print #window.te, d$; "  "; t$; "  "; DataLine$ ' display the string
              print #log, d$; "  "; t$; "  "; DataLine$       ' and write it to the file
              close #log

              Delimiter$ = Chr$(32)
              DevNum$ = ExtractField$(DataLine$, Delimiter$, 1)
              TCelcius$ = ExtractField$(DataLine$, Delimiter$, 2)
              SerNum$ = ExtractField$(DataLine$, Delimiter$, 3)

              DevNum = Val(DevNum$)
              TCelcius = Val(TCelcius$)
              print #window.te, DevNum;  "  "; TCelcius

              if (SerNum$ = "109DD748" AND TCelcius > 25.0) Then
                 print #window.te, SerNum$; " is at High Alarm"
              end if

              if (SerNum$ = "109DD748" AND TCelcius < 23.0) Then
                 print #window.te, SerNum$; " is at Low Alarm"
              end if

          case else
              print "Unknown error"   ' probably an overkill
              exit while

        end select

    wend
    do
        print #window.gb, "setfocus"
        scan ' used for check if activity from the keyboard
        T$ = time$()
        Delimiter$ = ":"
        CurrentMinute = Val(ExtractField$(T$, Delimiter$, 2))
    loop until (CurrentMinute = TimeoutMinute)

    goto [again]

[display]
    ' now open the file display the data
    open "d:\data.txt" for input as #log

    while eof(#log) = 0
       line input #log, datain$
       print #window.te, datain$
    wend

    close #log

[Done]
    Goto [Done]

[getChar]

    'Whenever the user presses a key, we go here to process it.
    c$ = Inkey$
    ' print #window.te, "at getChar"
    goto [display]

[closecomm]
     print "Error code: ";ComErrorNumber
     close #commhandle
     goto [top]

function GetLine$(ByRef StatusFlag)

    TimeOutTime = time$("milliseconds")
    TimeOutDate = date$("days")

    TimeOutTime = TimeOutTime + 3000 ' three seconds

    if (TimeOutTime >= 86400000) then
        TimeOutDate = TimeOutDate + 1
        TimeOutTime = TimeOutTime - 86400000
    end if


     DataLine$ = ""
     StatusFlag = 0
     NumChars = 0

     x=0
     while x=0
        if lof(#commhandle) <>0 then
           c$ = input$(#commhandle, 1)
           select case
              case  c$ = ">"
                 DataLine$ = c$
                 StatusFlag = 1 ' prompt
                 exit while

              case c$ = chr$(13)
                 StatusFlag = NumChars
                 exit while

              case c$ = chr$(10)    ' ignore it

              case else
                 DataLine$ = DataLine$ + c$
                 NumChars = NumChars + 1

            end select
        else
           CurrentDate = date$("days")
           CurrentTime = time$("milliseconds")
           If (CurrentTime >= TimeOutTime) AND (CurrentDate >= TimeOutDate) Then
              StatusFlag = -30000
              exit while
           end if
        end if
     wend
     GetLine$ = DataLine$
End Function

function ExtractField$(Var$, Delimiter$, NumField)

    for Num = 1 to NumField
       Index = FindDelimiter(Var$, Delimiter$)
       LeftStr$ = Left$(Var$, Index - 1) ' everything to the left of the space
       Var$ = Mid$(Var$, Index+1)        ' to the right of the space
       If Num = NumField Then
          exit for
       End If
    next

    ExtractField$ = LeftStr$

end function

function FindDelimiter(Var$, Delimiter$)

    for n = 1 to len(Var$)
       ch$ = Mid$(Var$, n, 1)
       If ch$ = Delimiter$ then
          exit for
       End If
       If ch$ = Chr$(13) then
          exit for
       End If
       If ch$ = Chr$(10) then
          exit for
       End If
    next
    FindDelimiter = n

end function

sub pause mil
    tcurrent = time$("milliseconds")
    timeout = tcurrent + mil
    if timeout > 86400000 then  ' roll over at midnight
       timeout = timeout - 86400000
       do
           tcurrent = time$("milliseconds")
       loop until (86400000 - tcurrent) > 1000000
    end if

    do
       tcurrent = time$("milliseconds")
    loop until (tcurrent >= timeout)

    end sub

    end