Page 1 of 2

DS18B20 Module

Posted: Tue Dec 17, 2013 11:59 am
by User_Russian
This module is designed to work with temperature sensors DS18B20 (1-Wire Interface) firm Dallas.

Image

This temperature sensor is connected to a serial port, through this adapter.

Image

Code: Select all

DeclareModule DS18B20

  ;-DeclareModule

  Enumeration
    #DS_Err_OK   ; Ошибок нет.
    #DS_Err_Port ; Нет открытого порта (для OpenPort() - ошибка открытия порта).
    #DS_Err_NoSensor ; Не найден датчик.
    #DS_Err_IO   ; Ошибка при обмене информацияей с датчиком.
    #DS_Err_BaudRate ; Ошибка при изменении скорости порта.
  EndEnumeration
  
  Structure DS_DataByte ; Копия содержимого ОЗУ датчика
    L_Termo.a       ; Младший байт температуры
    H_Termo.a       ; Старший байт температуры
    H_User.a        ; Вехрний предел температуры
    L_User.a        ; Нижний предел температуры
    Config.a        ; Байт конфигурации
    x1.a            ; Резерв (в датчике не реализовано)
    x2.a            ; Резерв (в датчике не реализовано)
    x3.a            ; Резерв (в датчике не реализовано)
    CRC.a           ; Контрольная сумма
  EndStructure
  
  #PortOnly = -10
  
  Declare GetLastErr()
  Declare.s GetLastErr_String(Err=-1)
  Declare ClosePort(Port=#PortOnly)
  Declare OpenPort(ComPort.s, Port=#PortOnly)
  Declare ResetDS(Port=#PortOnly)
  Declare.a ByteRW(Byte.a=$FF, Port=#PortOnly)
  Declare.a GetCRC(*Buff, Count)
  Declare GetThermo(*Info.DS_DataByte, Port=#PortOnly)
  
EndDeclareModule

Module DS18B20

EnableExplicit

;- Private

Structure CRC_Buff
  Byte.a[0]
EndStructure

CompilerIf #PB_Compiler_OS = #PB_OS_Linux
  #NCCS = 32
  #TCSANOW = 0
  
  Structure termios Align #PB_Structure_AlignC
    c_iflag.l     ; .l because .i changes size with the x64 version
    c_oflag.l
    c_cflag.l
    c_lflag.l
    c_line.a
    c_cc.a[#NCCS]
    c_ispeed.l
    c_ospeed.l
  EndStructure
CompilerEndIf

Threaded tLastErr=0
Global gCurrent_ComPort_ID=-1

Macro TestPortID(pID)
  If pID=#PortOnly
    pID=gCurrent_ComPort_ID
  EndIf
EndMacro

Macro Odd(xx)
  ((xx) & 1)
EndMacro

Procedure ClearInBuf(Port) ; Очистка приёмного буфера.
  Protected Count
  
  Count = AvailableSerialPortInput(Port)
  If Count>0
    Protected Dim Buff.a(Count+2)
    ReadSerialPortData(Port, @Buff(), Count)
  EndIf
EndProcedure

Procedure SetBaudRate(Port, Rate)
  Protected Result=#False, PortID

  If IsSerialPort(Port)
    
    PortID = SerialPortID(Port)
    
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      
      Protected dcb.DCB
      If PortID
        If GetCommState_(PortID, @dcb)  
          dcb\BaudRate  = Rate ; Требуемая скорость.
          dcb\fbits | %101000000010001
          If SetCommState_(PortID, @dcb)
            Result = #True
          EndIf
        EndIf
      EndIf
      
    CompilerElse
      
      Protected options.termios, Sp
      
      If Rate=9600 Or Rate=115200
        
        Select Rate
          Case 9600   : Sp = 13
          Case 115200 : Sp = 4098
        EndSelect
        
        If tcgetattr_(PortID, @options) = 0   ; читает параметры порта
          If cfsetispeed_(@options, Sp) = 0   ; установка скорости порта
            If cfsetospeed_(@options, Sp) = 0 ; установка скорости порта
              If tcsetattr_(PortID, #TCSANOW, @options) = 0
                Result=#True
              EndIf
            EndIf
          EndIf
        EndIf
        
      EndIf
      
    CompilerEndIf
    
    If Result=#False : tLastErr=#DS_Err_BaudRate : EndIf
  EndIf
    
  ProcedureReturn Result
EndProcedure


;-Public

Procedure GetLastErr()
  ProcedureReturn tLastErr
EndProcedure

Procedure.s GetLastErr_String(Err=-1)
  Protected String.s
  
  If Err=-1
    Err=tLastErr
  EndIf
  Select Err
    Case #DS_Err_OK : String = "Нет ошибок"
    Case #DS_Err_Port : String = "Не удалось открыть порт"
    Case #DS_Err_NoSensor : String = "Не найден датчик температуры"
    Case #DS_Err_IO : String = "Ошибка обмена данными с датчиком"
    Case #DS_Err_BaudRate : String = "Ошибка изменения скорости порта"
  EndSelect
    
  ProcedureReturn String
EndProcedure

Procedure ClosePort(Port=#PortOnly)
  Protected Result = #False
  
  TestPortID(Port)
  
  If IsSerialPort(Port)
    SetSerialPortStatus(Port, #PB_SerialPort_DTR, 0)
    SetSerialPortStatus(Port, #PB_SerialPort_RTS, 0)
    CloseSerialPort(Port)
    Result = #True
    tLastErr = #DS_Err_OK
  Else
    tLastErr = #DS_Err_Port
  EndIf
  
  ProcedureReturn Result
EndProcedure

Procedure OpenPort(ComPort.s, Port=#PortOnly)
  Protected ID, PortID
  
  If Port>=0
    ClosePort(Port)
    PortID=Port
  Else
    PortID=#PB_Any
  EndIf
  
  ID = OpenSerialPort(PortID, ComPort, 115200, #PB_SerialPort_NoParity,
                      8, 1, #PB_SerialPort_RtsHandshake, 24, 24)
  If ID
    If Port=#PortOnly
      gCurrent_ComPort_ID = ID
    ElseIf Port>=0
      ID=Port
    EndIf
    SetSerialPortStatus(ID, #PB_SerialPort_DTR, 1)
    SetSerialPortStatus(ID, #PB_SerialPort_RTS, 1)
    tLastErr = #DS_Err_OK
  Else
    tLastErr=#DS_Err_Port
  EndIf
  
  ProcedureReturn ID
EndProcedure

Procedure ResetDS(Port=#PortOnly) ; Подаём команду "Сброс" датчику температуры.
  Protected x, Result=#False
  
  TestPortID(Port)
  
  tLastErr=#DS_Err_OK
  
  If IsSerialPort(Port)
    If SetBaudRate(Port, 9600)
      
      ClearInBuf(Port)
      
      x=$F0
      WriteSerialPortData(Port, @x, 1)
      x = ElapsedMilliseconds()+100
      Repeat
        Delay(2)
      Until AvailableSerialPortInput(Port)>0 Or ElapsedMilliseconds()>x
      
      If AvailableSerialPortInput(Port)=1
        x=0
        If ReadSerialPortData(Port, @x, 1) = 1
          Result=Bool(x<>$F0)
          If Result=#False : tLastErr=#DS_Err_NoSensor : EndIf
        Else
          tLastErr=#DS_Err_NoSensor
        EndIf
      Else
        tLastErr=#DS_Err_NoSensor
      EndIf
      
      SetBaudRate(Port, 115200)
      
    EndIf
  Else
    tLastErr=#DS_Err_Port
  EndIf
  
  ProcedureReturn Result
EndProcedure

Procedure.a ByteRW(Byte.a=$FF, Port=#PortOnly)
  Protected Result.a, CRC.a, x.a, i, Time
  
 
  TestPortID(Port)
  
  tLastErr=#DS_Err_OK
  Result=$FF
  
  If IsSerialPort(Port)
    ClearInBuf(Port)
    
    Time=ElapsedMilliseconds()+100
    
    For i= 1 To 8
      
      x=Odd(Byte)*$FF
      WriteSerialPortData(Port, @x, 1)
      Byte>>1
      
      Repeat : Until AvailableSerialPortInput(Port)>0 Or ElapsedMilliseconds()>Time
      
      If AvailableSerialPortInput(Port)=1
        x=0
        If ReadSerialPortData(Port, @x, 1) = 1
          Result>>1
          If Odd(x) : Result | $80 : EndIf
        Else
          Result = $FF
          tLastErr=#DS_Err_IO
          Break
        EndIf
      Else
        Result = $FF
        tLastErr=#DS_Err_IO
        Break
      EndIf
      
    Next i
    
  Else
    tLastErr=#DS_Err_Port
  EndIf

  ProcedureReturn Result
EndProcedure

Procedure.a GetCRC(*Buff.CRC_Buff, Count)
  Protected CRC.a, Byte.a, i, x
  
  Count-1
  For i=0 To Count
    Byte=*Buff\Byte[i]
    For x=1 To 8
      If (Byte ! CRC) & 1
        CRC = ((CRC!$18)>>1)|$80
      Else
        CRC >> 1
      EndIf
      Byte>>1
    Next x
  Next i
  
  ProcedureReturn CRC
EndProcedure

Procedure GetThermo(*Info.CRC_Buff, Port=#PortOnly)
  Protected i, Result=#False
  
  tLastErr=#DS_Err_OK
  
  TestPortID(Port)
  If IsSerialPort(Port)
    ClearInBuf(Port)
    
    If ResetDS(Port)
      ByteRW($CC, Port)
      ByteRW($44, Port)
      Delay(800)
      If ResetDS(Port)
        ByteRW($CC, Port)
        ByteRW($BE, Port)
        
        For i=0 To 8
          *Info\Byte[i] = ByteRW($FF, Port)
        Next
        
        Result=#True
        
      EndIf
    EndIf
    
  Else
    tLastErr=#DS_Err_Port
  EndIf
    
  ProcedureReturn Result
EndProcedure

EndModule
For example, the use of the module was created a small program "DS18B20 Thermo".

Screenshot Windows-version.

Image

Screenshot Linux-version.

Image

Download files.

Re: DS18B20 Module

Posted: Tue Dec 17, 2013 3:30 pm
by Kukulkan
Great stuff! Thank you! :D

Re: DS18B20 Module

Posted: Thu Dec 19, 2013 12:03 am
by minimy
Really amazing. Thank you very much friend!
And merry christmas!

Большое спасибо друг! :wink:

Re: DS18B20 Module

Posted: Fri Dec 20, 2013 10:53 am
by dige
Thank you User_Russian!
Very nice, but could you please provide some more informations about the serial adapter?

Re: DS18B20 Module

Posted: Fri Dec 20, 2013 11:15 am
by User_Russian
dige wrote:Very nice, but could you please provide some more informations about the serial adapter?
What is needed, information?
The adapter is connected to a COM port on your computer.http://en.wikipedia.org/wiki/Serial_port
Or you need more detailed information about the components of the adapter?

Transistors, you can use any, n-p-n structure. For example BC546.
Diodes, any. For example 1N4007.
Zener VD4, the voltage of 4.7 - 5.6 volts.
Capacitor 22 microfarads.

Re: DS18B20 Module

Posted: Wed Sep 09, 2015 8:53 am
by Stephane Fonteyne
Hi,

The zip file is broken and not find on the server. It's about "download files"
Can you please send the complete zip file to my email. If you send a PM than give I my email address.
Thanks
Stephane

Re: DS18B20 Module

Posted: Wed Sep 09, 2015 10:49 am
by User_Russian
The file is downloaded normally.

I upload files on file sharing (storage period of 90 days) http://rghost.ru/75JMbbLgh

This improvement project. http://rghost.ru/6Zt6CYb6Q

Re: DS18B20 Module

Posted: Tue Sep 15, 2015 10:39 pm
by ElementE
Thank you User_Russian for making the improvement project (ThermoMon) available.

The ThermoMon source code shows how one can use PureBasic's advanced features in a practical way to interface with a temperature sensor.

Modules, Lists, Gui Window and other PB features are used in this well designed program.

Re: DS18B20 Module

Posted: Tue Sep 15, 2015 11:09 pm
by Joakim Christiansen
Cool to see people writing PB code for these, I have one connected to my ESP8266 to control a relay connected to another ESP8266. Basically creating my own "smart home". :)

Re: DS18B20 Module

Posted: Wed Sep 16, 2015 2:01 pm
by ElementE
In the ThermoMon source code the SelectPort procedure contains the following error message code:

Code: Select all

      StatusBarText(#StatusBar_Main, #StatusBar_Main_Status,
                     "Не удалось открыть порт '"+Port+
                     "'  Возможно он зянят или отстуствует в системе")
Google Translate could not translate the word "зянят" into English.

Perhaps someone who knows Russian could provide a translation?

Re: DS18B20 Module

Posted: Wed Sep 16, 2015 2:22 pm
by User_Russian
In this word spelling error. Must be "занят". In English this "busy".

Re: DS18B20 Module

Posted: Wed Sep 16, 2015 2:32 pm
by ElementE
Thank you User_Russian.

Re: DS18B20 Module

Posted: Fri Oct 02, 2015 12:09 pm
by ElementE
Maxim makes the DS2480B which implements an interface between the RS-232 and One-Wire protocols.

According to the description of the chip, "Internal timers relieve the host of the burden of generating the time-critical 1-Wire communication waveforms."

The datasheet:
"DS2480B, Serial to 1-Wire Line Driver"
https://www.maximintegrated.com/en/prod ... 2480B.html

The App Note:
Application Note 192: "Using the DS2480B Serial 1-Wire Driver"
http://pdfserv.maximintegrated.com/en/an/AN192.pdf

Re: DS18B20 Module

Posted: Fri Oct 02, 2015 1:29 pm
by ElementE
The "Denkovi Assembly Electronics" company (Bulgaria) sells on eBay a "USB to One Wire Interface module" that can be used to develop and test a Purebasic program without a person having to build any hardware.

The interface module is sold along with a DS18S20 temperature sensor.

User Manual:
http://denkovi.com/Documents/USB-One-Wi ... module.pdf

Re: DS18B20 Module

Posted: Tue Jan 05, 2016 10:51 pm
by User_Russian
Added support to Mac OS X. Now it supports all (Windows, Linux and Mac OS X).

OWire_Module.pbi

Code: Select all

DeclareModule OWire

  ;-DeclareModule

  Enumeration OW_ErrCode
    #OW_Err_OK        ; Ошибок нет.
    #OW_Err_Port      ; Нет открытого порта (для OpenPort() - ошибка открытия порта).
    #OW_Err_NoDevice  ; Не найдены 1-Wire устройства.
    #OW_Err_IO        ; Ошибка при обмене данными с 1-Wire устройством.
    #OW_Err_BaudRate  ; Ошибка при изменении скорости порта.
    #OW_Err_Search    ; Ошибка происходит если во время поиска ни одно 1-Wire устройство не ответило.
    #OW_Err_BadParam  ; Неккоректный параметр.
    #OW_Err_NoData    ; Нет данных.
  EndEnumeration
  
  #OW_PortOnly = -10 ; Идентификатор порта по умолчанию.
  
  
  #OW_Mode_CPU_MinUsage = 1  ; Уменьшить загрузку процессора (снизится скорость обмена данными).
  #OW_Mode_Invert       = 2  ; Инвертирование сигналов DTR и RTS.
  #OW_Mode_NoControlPin = 4  ; Не изменять состояние управляющих выходов DTR и RTS.
  
  Structure OW_RAM ; Копия содержимого ОЗУ датчика DS18B20 ($BE).
    L_Termo.a       ; Младший байт температуры
    H_Termo.a       ; Старший байт температуры
    Max_Termo.a     ; Верхний предел температуры
    Min_Termo.a     ; Нижний предел температуры
    Config.a        ; Байт конфигурации
    x.a[3]          ; Резерв 3 байта (в датчике не реализовано)
    CRC.a           ; Контрольная сумма
  EndStructure
;     
  Structure OW_Buff
    Byte.a[0]
  EndStructure
  
  Structure OW_SN  ; Идентификатор датчика ($33).
    Type.a          ; Тип устройства (код семейства)
    Number.a[6]     ; Серийный номер.
    CRC.a           ; Контрольная сумма.
  EndStructure
  
  
  Declare OW_GetMode()
  Declare OW_SetMode(Mode)
  Declare OW_GetPortDefID()
  Declare OW_GetLastError() ; Код ошибки (см. константы #OW_Err_xxxx)
  Declare OW_SetLastError(ErrCode)
  Declare OW_ClosePort(Port=#OW_PortOnly) ; Закрытие порта.
  Declare OW_OpenPort(ComPort.s, Port=#OW_PortOnly) ; Открытие порта.
  Declare OW_PortID(Port=#OW_PortOnly)
  Declare OW_Reset(Port=#OW_PortOnly)   ; Сброс датчика.
  Declare.a OW_ByteRW(Byte.a=$FF, Port=#OW_PortOnly) ; Чтение/запись байта.
  Declare OW_ReadMultiByte(*Buff.OW_Buff, Count, Port=#OW_PortOnly) ; Чтение массива байт.
  Declare OW_WriteMultiByte(*Buff.OW_Buff, Count, Port=#OW_PortOnly) ; Запись массива байт.
  Declare.a OW_GetCRC(*Buff, Count)       ; Вычислене контрольной суммы.
    
  ; Термометр передает 8-битный код семейства (10h для DS18S20 и 28h для DS18B20),
  ; затем 48-битный серийный номер, а затем 8-битную CRC для проверки правильности принятой информации.
  Declare OW_ReadROM(*Info.OW_SN, Port=#OW_PortOnly)
  
  ; Эта процедура позволяет адресовать на шине конкретный термометр.
  ; Необходимо передать 64-битный код датчика (считанный процедурой ReadROM()),
  ; и только тот, который имеет такой код, будет «откликаться» до следующего импульса сброса.
  Declare OW_SelectROM(*Info.OW_SN, Port=#OW_PortOnly)
  
  ; Вызов процедуры позволит пропустить процедуру сравнения серийного номера
  ; и тем самым сэкономить время в системах, где на шине имеется всего одно устройство.
  Declare OW_SkipROM(Port=#OW_PortOnly)
  
  ; Поиск датчиков, идентификаторы которых будут помещены в список.
  Declare OW_SearchROM_Reset()
  Declare OW_SearchROM_Enum(*Sensor.OW_SN, Port=#OW_PortOnly)
  
  ; Поиск датчиков у которых результат последнего измерения температуры
  ; выходит за предустановленные пределы TH и TL.
  Declare OW_SearchAlarm_Reset()
  Declare OW_SearchAlarm_Enum(*Sensor.OW_SN, Port=#OW_PortOnly)
  
  ; Чтение данных из промежуточного ОЗУ датчика.
  Declare OW_ReadRAM(*Info.OW_RAM, Port=#OW_PortOnly)
  
  ; Запись данных в промежуточное ОЗУ датчика
  Declare OW_WriteRAM(*Info.OW_Buff, Port=#OW_PortOnly)
  
  ; Копирование данных из промежуточного ОЗУ в энергонезависимую память датчика.
  ; Эта операция требует около 10мс.
  Declare OW_CopyRAM(Port=#OW_PortOnly)
  
  ; Запуск процесса преобразования температуры.
  Declare OW_ConvertT(Wait=0, Port=#OW_PortOnly)
  
  ; Эта процедура действует обратным образом по отношению к процедуре OW_CopyRAM(),
  ; т.е. она позволяет считать байты TH и TL из энергонезависимой памяти в промежуточное ОЗУ.
  Declare OW_Recall(Port=#OW_PortOnly)
  
  ; Проверка, использует ли 1-Wire устройство паразитное питание.
  Declare OW_ReadPowerSupply(Port=#OW_PortOnly)
  
EndDeclareModule

Module OWire

EnableExplicit

;- Private

#wbf_WaitByte  = 1
#wbf_ClearByte = 2
#wbf_Slot      = 4

CompilerIf #PB_Compiler_OS = #PB_OS_Linux Or #PB_Compiler_OS = #PB_OS_MacOS
  #NCCS = 32
  #TCSANOW = 0
  
  Structure termios Align #PB_Structure_AlignC
    c_iflag.l
    c_oflag.l
    c_cflag.l
    c_lflag.l
    c_line.a
    c_cc.a[#NCCS]
    c_ispeed.l
    c_ospeed.l
  EndStructure
  
  Global gModuleModeLin=0
  
CompilerEndIf

Structure sSearch
  LastDiscrepancy.l
  Done.a
  Search.a[8]
EndStructure

CompilerIf #PB_Compiler_Thread
  Threaded tLastErr=0, tSearchROM.sSearch, tSearchAlarm.sSearch
CompilerElse
  Global tLastErr=0, tSearchROM.sSearch, tSearchAlarm.sSearch
CompilerEndIf

Global gCurrent_ComPort_ID=-2, gModuleMode=0

Macro TestPortID(pID)
  If pID=#OW_PortOnly
    pID=gCurrent_ComPort_ID
  EndIf
EndMacro 

Macro Odd(xx)
  ((xx) & 1)
EndMacro

Procedure ClearInBuf(Port) ; Очистка приёмного буфера.
  Protected Count, *Mem
  
  Count = AvailableSerialPortInput(Port)
  If Count>0
    *Mem=AllocateMemory(Count+2, #PB_Memory_NoClear)
    If *Mem
      ReadSerialPortData(Port, *Mem, Count)
      FreeMemory(*Mem)
    EndIf
  EndIf
  
EndProcedure

Procedure TestPort(Port)
  
  TestPortID(Port)
  
  If IsSerialPort(Port)
    tLastErr=#OW_Err_OK
    ClearInBuf(Port)
  Else
    tLastErr=#OW_Err_Port
  EndIf
  
  ProcedureReturn Port
EndProcedure

Procedure SetBaudRate(Port, Rate)
  Protected Result=#False, PortID

  If IsSerialPort(Port)
    
    PortID = SerialPortID(Port)
    
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      
      Protected dcb.DCB
      If PortID
        If GetCommState_(PortID, @dcb)  
          dcb\BaudRate  = Rate ; Требуемая скорость.
          If gModuleMode & #OW_Mode_NoControlPin = 0
            dcb\fbits & ~%0010000000100000
            If gModuleMode & #OW_Mode_Invert = 0
              dcb\fbits |  %0001000000010000
            Else
              dcb\fbits & ~%0001000000010000
            EndIf
          EndIf
          If SetCommState_(PortID, @dcb)
            Result = #True
          EndIf
        EndIf
      EndIf
      
    CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux Or #PB_Compiler_OS = #PB_OS_MacOS
      
      Protected options.termios, Sp
      
      If Rate=9600 Or Rate=115200
        
        CompilerIf #PB_Compiler_OS = #PB_OS_Linux
          Select Rate
            Case 9600   : Sp = 13
            Case 115200 : Sp = 4098
          EndSelect
        CompilerElse
          Sp = Rate
        CompilerEndIf
        
        If tcgetattr_(PortID, @options) = 0   ; читает параметры порта.
          If cfsetispeed_(@options, Sp) = 0   ; установка скорости порта.
            If cfsetospeed_(@options, Sp) = 0 ; установка скорости порта.
              If tcsetattr_(PortID, #TCSANOW, @options) = 0
                Result=#True
              EndIf
            EndIf
          EndIf
        EndIf
        
      EndIf
      
      If gModuleMode & #OW_Mode_NoControlPin = 0
        If gModuleMode&#OW_Mode_Invert <> gModuleModeLin&#OW_Mode_Invert
          If gModuleMode & #OW_Mode_Invert=0
            SetSerialPortStatus(Port, #PB_SerialPort_DTR, 1)
            SetSerialPortStatus(Port, #PB_SerialPort_RTS, 1)
          Else
            SetSerialPortStatus(Port, #PB_SerialPort_DTR, 0)
            SetSerialPortStatus(Port, #PB_SerialPort_RTS, 0)
          EndIf
        EndIf
        gModuleModeLin=gModuleMode
      EndIf
      
    CompilerEndIf
    
    If Result=#False : tLastErr=#OW_Err_BaudRate : EndIf
  EndIf
    
  ProcedureReturn Result
EndProcedure


Procedure.a ReadBit(Port, Flag=#wbf_WaitByte|#wbf_ClearByte|#wbf_Slot)
  Protected Bit.a, x.l
  
  tLastErr=#OW_Err_OK
  
  If Flag&#wbf_ClearByte
    ClearInBuf(Port)
  EndIf
  
  Bit=$FF
  If Flag&#wbf_Slot
    If WriteSerialPortData(Port, @Bit, 1)<>1
      tLastErr=#OW_Err_IO
      ProcedureReturn 0
    EndIf
  EndIf
  
  If Flag&#wbf_WaitByte
    x = ElapsedMilliseconds()
    Repeat
      If gModuleMode & #OW_Mode_CPU_MinUsage : Delay(1) : EndIf
    Until AvailableSerialPortInput(Port)>0 Or Int(ElapsedMilliseconds()-x)>=36
  EndIf
  
  If AvailableSerialPortInput(Port)=1
    x=0
    If ReadSerialPortData(Port, @x, 1) = 1
      Bit = Bool(x=$FF)
    Else
      tLastErr=#OW_Err_IO
    EndIf
  Else
    tLastErr=#OW_Err_IO
  EndIf
  
  ProcedureReturn Odd(Bit)
EndProcedure


Procedure WriteBit(Bit.a, Port, Flag=#wbf_WaitByte|#wbf_ClearByte)
  Bit=Odd(Bit)*$FF
  WriteSerialPortData(Port, @Bit, 1)
  If Flag & #wbf_WaitByte
    Protected x.l = ElapsedMilliseconds()
    Repeat
      If gModuleMode & #OW_Mode_CPU_MinUsage : Delay(1) : EndIf
    Until AvailableSerialPortInput(Port)>0 Or Int(ElapsedMilliseconds()-x)>=36
    If Flag & #wbf_ClearByte And AvailableSerialPortInput(Port)>0
      ReadSerialPortData(Port, @Bit, 1)
    EndIf
  EndIf
EndProcedure

; Код на основе материала. http://www.maximintegrated.com/app-notes/index.mvp/id/187
Procedure SearchDevice(*Sensor.OW_SN, *Search.sSearch, Port, Code.a)

  Protected Result=#False
  Protected.l id_bit_number, last_zero
  Protected.l rom_byte_number
  Protected.l id_bit, cmp_id_bit
  Protected.a rom_byte_mask, search_direction
  
  If *Search\Done = #False
    
    ClearInBuf(Port)
    
    
    id_bit_number = 1
    last_zero = 0
    rom_byte_number = 0
    rom_byte_mask = 1
    
    If OW_Reset(Port)
      
      OW_WriteMultiByte(@Code, 1, Port)
      
      If tLastErr=#OW_Err_OK
        
        Repeat
          
          id_bit = ReadBit(Port)
          If tLastErr<>#OW_Err_OK : Break : EndIf
          
          cmp_id_bit = ReadBit(Port)
          If tLastErr<>#OW_Err_OK : Break : EndIf
          
          If id_bit And cmp_id_bit ; Ни один датчик не ответил!
            
            tLastErr=#OW_Err_Search
            Break
          ElseIf id_bit <> cmp_id_bit
            search_direction = id_bit
          Else  ; Произошла коллизия (больше одного датчика и у них разные ID).
            
            If id_bit_number < *Search\LastDiscrepancy
              search_direction = Bool((*Search\Search[rom_byte_number] & rom_byte_mask))
            Else
              search_direction = Bool(id_bit_number=*Search\LastDiscrepancy)
            EndIf
            
            If search_direction = 0
              last_zero = id_bit_number
            EndIf
            
          EndIf
          
          If search_direction
            *Search\Search[rom_byte_number] | rom_byte_mask
          Else
            *Search\Search[rom_byte_number] & ~rom_byte_mask
          EndIf
          
          WriteBit(search_direction, Port)
          
          id_bit_number+1
          rom_byte_mask<<1
          
          If rom_byte_mask=0
            rom_byte_number+1
            rom_byte_mask = 1
          EndIf
          
        Until rom_byte_number >= 8
        
        If Not id_bit_number < 65
          *Search\LastDiscrepancy = last_zero
          If *Search\LastDiscrepancy = 0
            *Search\Done = #True
          EndIf
          
          CopyMemory(@*Search\Search[0], *Sensor, 8)
          Result=#True
        EndIf
        
      EndIf
    EndIf
    
  Else
    tLastErr=#OW_Err_NoData
  EndIf
  
  ProcedureReturn Result
EndProcedure



;-Public

Procedure OW_GetMode()
  ProcedureReturn gModuleMode
EndProcedure

Procedure OW_SetMode(Mode)
  CompilerIf #PB_Compiler_OS = #PB_OS_Linux Or #PB_Compiler_OS = #PB_OS_MacOS
    gModuleModeLin=gModuleMode
  CompilerEndIf
  gModuleMode = Mode
EndProcedure

Procedure OW_GetPortDefID()
  ProcedureReturn gCurrent_ComPort_ID
EndProcedure

Procedure OW_GetLastError()
  ProcedureReturn tLastErr
EndProcedure

Procedure OW_SetLastError(ErrCode)
  tLastErr = ErrCode
EndProcedure

Procedure OW_ClosePort(Port=#OW_PortOnly)
  Protected Result = #False, OldPort
  
  OldPort=Port
  TestPortID(Port)
  
  If IsSerialPort(Port)
    If gModuleMode & #OW_Mode_NoControlPin = 0
      If gModuleMode & #OW_Mode_Invert = 0
        SetSerialPortStatus(Port, #PB_SerialPort_DTR, 0)
        SetSerialPortStatus(Port, #PB_SerialPort_RTS, 0)
      Else
        SetSerialPortStatus(Port, #PB_SerialPort_DTR, 1)
        SetSerialPortStatus(Port, #PB_SerialPort_RTS, 1)
      EndIf
    EndIf
    CloseSerialPort(Port)
    If OldPort=#OW_PortOnly
      gCurrent_ComPort_ID=-2
    EndIf
    Result = #True
    tLastErr = #OW_Err_OK
  Else
    tLastErr = #OW_Err_Port
  EndIf
  
  ProcedureReturn Result
EndProcedure

Procedure OW_OpenPort(ComPort.s, Port=#OW_PortOnly)
  Protected ID=0, PortID=-2
  
  If Port>=0 And Port<1000
    OW_ClosePort(Port)
    PortID=Port
  ElseIf Port=#OW_PortOnly 
    OW_ClosePort(Port)
    PortID=#PB_Any
  ElseIf Port=#PB_Any
    PortID=#PB_Any
  Else  
    tLastErr=#OW_Err_BadParam ; Неправильный идентификатор порта
    ProcedureReturn 0
  EndIf
  
  ID = OpenSerialPort(PortID, ComPort, 115200, #PB_SerialPort_NoParity,
                      8, 1, #PB_SerialPort_NoHandshake, 255, 255)
  If ID
    
    If Port<0
      If Port=#OW_PortOnly
        gCurrent_ComPort_ID = ID
      EndIf
      Port=ID
    EndIf
    
    If gModuleMode & #OW_Mode_NoControlPin = 0
      If gModuleMode & #OW_Mode_Invert = 0
        SetSerialPortStatus(Port, #PB_SerialPort_DTR, 1)
        SetSerialPortStatus(Port, #PB_SerialPort_RTS, 1)
      Else
        SetSerialPortStatus(Port, #PB_SerialPort_DTR, 0)
        SetSerialPortStatus(Port, #PB_SerialPort_RTS, 0)
      EndIf
      Delay(20)
    EndIf
    ;SerialPortTimeouts(Port, 10, 10, 2, 2, 10)
    OW_ByteRW($FF, Port)
    ClearInBuf(Port)
    
    tLastErr = #OW_Err_OK
    
  Else
    tLastErr=#OW_Err_Port
  EndIf
  
  ProcedureReturn ID
EndProcedure


Procedure OW_PortID(Port=#OW_PortOnly)
  Protected ID=0
  
  Port=TestPort(Port)
  If tLastErr = #OW_Err_OK
    ID = SerialPortID(Port)
  EndIf
  
  ProcedureReturn ID
EndProcedure

Procedure OW_Reset(Port=#OW_PortOnly) ; Подаём команду "Сброс" датчику температуры.
  Protected x.l, Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr = #OW_Err_OK
    If SetBaudRate(Port, 9600)
      
      ClearInBuf(Port)
      
      x=$F0
      If WriteSerialPortData(Port, @x, 1)=1
        
        x = ElapsedMilliseconds()
        Repeat
          Delay(2)
        Until AvailableSerialPortInput(Port)>0 Or Int(ElapsedMilliseconds()-x)>=32
        
        If AvailableSerialPortInput(Port)=1
          x=0
          If ReadSerialPortData(Port, @x, 1) = 1
            Result=Bool(x<>$F0)
            If Result=#False : tLastErr=#OW_Err_NoDevice : EndIf
          Else
            Goto M1:
          EndIf
        Else
          M1:
          tLastErr=#OW_Err_IO
          ClearInBuf(Port)
        EndIf
        
      Else
        tLastErr=#OW_Err_IO
      EndIf
      
      SetBaudRate(Port, 115200)
      
    EndIf
  EndIf
  
  ProcedureReturn Result
EndProcedure


Procedure.a OW_ByteRW(Byte.a=$FF, Port=#OW_PortOnly)
  Protected Result.a, x.a, i
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK  
    For i= 1 To 8

      WriteBit(Odd(Byte), Port, #wbf_WaitByte)
      Byte>>1
      
      x=ReadBit(Port, 0)
      If tLastErr=#OW_Err_OK
        Result>>1
        Result|(x<<7)
      Else
        Result = $FF
        Break
      EndIf

    Next i
  EndIf

  ProcedureReturn Result
EndProcedure

Procedure OW_ReadMultiByte(*Buff.OW_Buff, Count, Port=#OW_PortOnly) ; Чтение массива байт.
  Protected Result=#False, Time, WaitTime
  Protected RealCount, i, x, Byte.a, Pos, *Mem.OW_Buff
  
  If *Buff And Count>0 And Count<1000000
       
    Port=TestPort(Port)
    
    If tLastErr=#OW_Err_OK
      
      RealCount=Count*8

      WaitTime=36+(Count*2)
      Time = ElapsedMilliseconds()
      
      *Mem=AllocateMemory(RealCount+2, #PB_Memory_NoClear)
      
      If *Mem
        
        FillMemory(*Mem, RealCount, $FF, #PB_Ascii)
        
        If WriteSerialPortData(Port, *Mem, RealCount)=RealCount
          
          Repeat
            If gModuleMode & #OW_Mode_CPU_MinUsage : Delay(2) : EndIf
          Until Int(ElapsedMilliseconds()-Time)>=WaitTime Or AvailableSerialPortInput(Port)>=RealCount
          
          If AvailableSerialPortInput(Port)=RealCount
            
            FillMemory(*Mem, RealCount, 0, #PB_Ascii)
            
            If ReadSerialPortData(Port, *Mem, RealCount) = RealCount
              Pos=0
              For i=0 To Count-1
                Byte=0
                For x=0 To 7
                  Byte>>1
                  Byte | (Bool(*Mem\Byte[Pos]=$FF)<<7)
                  Pos+1
                Next x
                *Buff\Byte[i]=Byte : 
              Next i
              Result=#True
            Else
              tLastErr=#OW_Err_IO
            EndIf
            
          Else
            ClearInBuf(Port)
            tLastErr=#OW_Err_IO
          EndIf
          
        Else
          tLastErr=#OW_Err_IO
        EndIf
        
        FreeMemory(*Mem)
        
      Else
        tLastErr=#OW_Err_BadParam
      EndIf
      
    Else
      tLastErr=#OW_Err_Port
    EndIf
  Else
    tLastErr=#OW_Err_BadParam
  EndIf
  
  ProcedureReturn Result
EndProcedure

Procedure OW_WriteMultiByte(*Buff.OW_Buff, Count, Port=#OW_PortOnly) ; Запись массива байт.
  Protected Result=#False, i, x, Byte.a, RealCount
  Protected Pos, *Mem.OW_Buff
  
  If (*Buff And Count>0 And Count<1000000) Or (*Buff=0 And Count=1)
    Port=TestPort(Port)
    
    If tLastErr=#OW_Err_OK 
      
      
      RealCount=Count*8
      
      *Mem=AllocateMemory(RealCount+2, #PB_Memory_NoClear)
      If *Mem
        
        Pos=0
        For x=0 To Count-1
          
          If Count=1 And *Buff&~$FF=0
            Byte=*Buff
          Else
            Byte=*Buff\Byte[x]
          EndIf
          For i=1 To 8
            *Mem\Byte[Pos]=Odd(Byte)*$FF
            Pos+1
            Byte>>1
          Next i
        Next x
        
        If WriteSerialPortData(Port, *Mem, RealCount)=RealCount
          
          x=36+(Count*2)
          i = ElapsedMilliseconds()
          
          Repeat
            If gModuleMode & #OW_Mode_CPU_MinUsage : Delay(2) : EndIf
          Until Int(ElapsedMilliseconds()-i)>=x Or AvailableSerialPortInput(Port)>=RealCount
          
          If AvailableSerialPortInput(Port)=RealCount
            If ReadSerialPortData(Port, *Mem, RealCount) = RealCount
              Result=#True
            Else
              tLastErr=#OW_Err_IO
            EndIf
          Else
            ClearInBuf(Port)
            tLastErr=#OW_Err_IO
          EndIf
          
        Else
          ClearInBuf(Port)
          tLastErr=#OW_Err_IO
        EndIf
        
        FreeMemory(*Mem)
        
      Else
        tLastErr=#OW_Err_BadParam
      EndIf
      
    EndIf
    
  Else
    tLastErr=#OW_Err_BadParam
  EndIf
  
  ProcedureReturn Result
EndProcedure


Procedure.a OW_GetCRC(*Buff.OW_Buff, Count)
  Protected CRC.a, Byte.a, i, x
  
  If *Buff And Count>0
    
    tLastErr=#OW_Err_OK
    
    Count-1
    For i=0 To Count
      Byte=*Buff\Byte[i]
      For x=1 To 8
        If (Byte ! CRC) & 1
          CRC = ((CRC!$18)>>1)|$80
        Else
          CRC >> 1
        EndIf
        Byte>>1
      Next x
    Next i
    
  Else
    tLastErr=#OW_Err_BadParam
  EndIf
  
  ProcedureReturn CRC
EndProcedure

; Датчик передает 8-битный код семейства (10h для DS18S20 и 28h для DS18B20),
; затем 48-битный серийный номер, а затем 8-битную CRC для проверки правильности принятой информации.
Procedure OW_ReadROM(*Info.OW_Buff, Port=#OW_PortOnly)
  Protected i, Result=#False
  
  Port=TestPort(Port)
   
  If tLastErr=#OW_Err_OK
    If *Info
      If OW_Reset(Port)
        If OW_WriteMultiByte($33, 1, Port)
          If OW_ReadMultiByte(@*Info\Byte[0], 8, Port)
            Result=#True
          EndIf
        EndIf
      EndIf
    Else
      tLastErr=#OW_Err_BadParam
    EndIf
  EndIf
  
  ProcedureReturn Result
EndProcedure

; Эта процедура позволяет адресовать на шине конкретный датчик температуры.
; Необходимо передать 64-битный код датчика (считанный процедурой OW_ReadROM()),
; и только тот, который имеет такой код, будет «откликаться» до следующего импульса сброса.
Procedure OW_SelectROM(*Info.OW_Buff, Port=#OW_PortOnly)
  Protected i, Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK
    If *Info
      If OW_Reset(Port)      
        If OW_WriteMultiByte($55, 1, Port)
          If OW_WriteMultiByte(*Info, 8, Port)
            Result=#True
          EndIf
        EndIf
      EndIf
    Else
      tLastErr=#OW_Err_BadParam
    EndIf
  EndIf
  
  ProcedureReturn Result
EndProcedure

; Вызов процедуры позволит пропустить процедуру сравнения серийного номера
; и тем самым сэкономить время в системах, где на шине имеется всего одно устройство.
Procedure OW_SkipROM(Port=#OW_PortOnly)
  Protected Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK
    If OW_Reset(Port)
      If OW_WriteMultiByte($CC, 1, Port)
        Result=#True
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn Result
EndProcedure


Procedure OW_SearchROM_Reset()
  tLastErr=#OW_Err_OK
  FillMemory(@tSearchROM, SizeOf(sSearch))
  ProcedureReturn #True
EndProcedure

Procedure OW_SearchROM_Enum(*Sensor.OW_SN, Port=#OW_PortOnly)
  Protected Result=#False
  
  If *Sensor
    Port=TestPort(Port)
    If tLastErr=#OW_Err_OK
      Result=SearchDevice(*Sensor, @tSearchROM, Port, $F0)
    EndIf
  Else
    tLastErr=#OW_Err_BadParam
  EndIf
    
  ProcedureReturn Result
EndProcedure


Procedure OW_SearchAlarm_Reset()
  tLastErr=#OW_Err_OK
  FillMemory(@tSearchAlarm, SizeOf(sSearch))
  ProcedureReturn #True
EndProcedure

Procedure OW_SearchAlarm_Enum(*Sensor.OW_SN, Port=#OW_PortOnly)
  Protected Result=#False
  
  If *Sensor
    Port=TestPort(Port)
    If tLastErr=#OW_Err_OK
      Result=SearchDevice(*Sensor, @tSearchAlarm, Port, $EC)
    EndIf
  Else
    tLastErr=#OW_Err_BadParam
  EndIf
    
  ProcedureReturn Result
EndProcedure


; Чтение данных из промежуточного ОЗУ датчика.
Procedure OW_ReadRAM(*Info.OW_Buff, Port=#OW_PortOnly)
  Protected i, Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK
    If *Info
      If OW_WriteMultiByte($BE, 1, Port)
        If OW_ReadMultiByte(*Info, 9, Port) : Result=#True : EndIf
      EndIf
    Else
      tLastErr=#OW_Err_BadParam
    EndIf
  EndIf
  
  ProcedureReturn Result
EndProcedure

; Запись данных в промежуточное ОЗУ датчика
Procedure OW_WriteRAM(*Info.OW_Buff, Port=#OW_PortOnly)
  Protected i, Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK
    If *Info
      If OW_WriteMultiByte($4E, 1, Port)
        
        For i=0 To 2
          OW_ByteRW(*Info\Byte[i], Port)
          If tLastErr<>#OW_Err_OK : Break : EndIf
        Next
        
        If tLastErr=#OW_Err_OK : Result=#True : EndIf
        
      EndIf
    Else
      tLastErr=#OW_Err_BadParam
    EndIf
  EndIf
   
  ProcedureReturn Result
EndProcedure

; Копирование данных из промежуточного ОЗУ в энергонезависимую память датчика.
; Это требует около 10мс.
Procedure OW_CopyRAM(Port=#OW_PortOnly)
  Protected Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK
    If OW_WriteMultiByte($48, 1, Port) 
      Delay(20)
      Result=#True 
    EndIf
  EndIf
    
  ProcedureReturn Result
EndProcedure

; Запуск процесса преобразования температуры.
Procedure OW_ConvertT(Wait=0, Port=#OW_PortOnly)
  Protected Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK
    If OW_WriteMultiByte($44, 1, Port)
      Result=#True
      If Wait>0
        Delay(Wait)
      EndIf
    EndIf
  EndIf
    
  ProcedureReturn Result
EndProcedure

; Эта процедура действует обратным образом по отношению к процедуре OW_CopyRAM(),
; т.е. она позволяет считать байты TH и TL из энергонезависимой памяти в промежуточное ОЗУ.
Procedure OW_Recall(Port=#OW_PortOnly)
  Protected Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK
    If OW_WriteMultiByte($B8, 1, Port) : Result=#True : EndIf
  EndIf
    
  ProcedureReturn Result
EndProcedure

; Проверка, использует ли 1-Wire устройство "паразитное" питание.
Procedure OW_ReadPowerSupply(Port=#OW_PortOnly)
  Protected Result=#False
  
  Port=TestPort(Port)
  
  If tLastErr=#OW_Err_OK
    If OW_WriteMultiByte($B4, 1, Port)
      Result=ReadBit(Port)
    EndIf
  EndIf
  
  ProcedureReturn Result
EndProcedure

EndModule

Example

Code: Select all

XIncludeFile "OWire_Module.pbi"

UseModule OWire

CompilerIf #PB_Compiler_Thread = 0
  CompilerError "Please enable thread-safe"
CompilerEndIf

EnableExplicit

Enumeration Window
  #WinMain
EndEnumeration

Enumeration Menu
  #Menu_Main
EndEnumeration

Enumeration Gadget
  #ComboPort
  #SpinThermoInterval
  #CheckThermoControl
  #ContainerThermoControl
  #SpinThermoMin
  #SpinThermoMax
  #Table
EndEnumeration

Enumeration
  #MItem_Log
  #MItem_Exit
  #MItem_Mode_CPU_MinUsage
  #MItem_Mode_Invert
  #MItem_ConvertOnly
  #MItem_Shem
  #MItem_About
EndEnumeration

Enumeration StatusBar
  #StatusBar_Main
EndEnumeration

Enumeration
  #StatusBar_Main_Status
EndEnumeration

Enumeration
  #Table_Type
  #Table_SN
  #Table_Power
  #Table_Thermo
  #Table_Status
EndEnumeration

Enumeration Image
  #ImageShem
EndEnumeration

Enumeration File
  #LogFileID
EndEnumeration

#Table_Color_Err  = $8888FD
#Table_Color_Warn = $ABF9FF

#DI_ErrFlag_CRC_ROM  = 1
#DI_ErrFlag_CRC_RAM  = 1<<1
#DI_ErrFlag_Thermo   = 1<<2
#DI_ErrFlag_OW       = 1<<3

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  #PortName = "COM"
CompilerElse
  #PortName = "/dev/ttyS"
CompilerEndIf

Structure ThreadParam
  BreakTh.a      ; Если #True, то нужно завершить работу потока.
  PortChange.a   ; Если #True, то выбран другой порт.
  ThermoControl.a
  ConvertOnly.a
  ThermoInterval.u ; Интервал в секундах опроса датчиков.
  ThermoMin.w
  ThermoMax.w
  Log.a
EndStructure

Structure DevicesInfo
  SN.OW_SN
  Thermo.f
  ErrFlag.a
EndStructure

Define Thread_OW_ID, Event, Window, x

Global gOW_Thread.ThreadParam
gOW_Thread\BreakTh    = #False
gOW_Thread\PortChange = #True

Procedure.s GetLastErrorString(Err)
  Protected String.s
  
  Select Err
    Case #OW_Err_OK       : String = "Нет ошибок"
    Case #OW_Err_Port     : String = "Нет открытого COM порта"
    Case #OW_Err_NoDevice : String = "Нет ответа от '1-Wire' устройств"
    Case #OW_Err_IO       : String = "Ошибка обмена данными"
    Case #OW_Err_BaudRate : String = "Ошибка изменения скорости порта"
    Case #OW_Err_Search   : String = "Ошибка при поиске '1-Wire' устройств"
    Case #OW_Err_BadParam : String = "Ошибка в аргументе функции"
    Case #OW_Err_NoData   : String = "Нет данных"
  EndSelect
  
  ProcedureReturn String
EndProcedure

Procedure WriteLog(String.s)
  If IsFile(#LogFileID)=0
    If OpenFile(#LogFileID, "DS18B20_ThermoMon.log")
      If Lof(#LogFileID)<=0
        WriteStringFormat(#LogFileID, #PB_UTF8)
      Else
        FileSeek(#LogFileID, Lof(#LogFileID))
      EndIf
      Goto _next
    EndIf
  Else
    _next:
    If String<>""
      ReplaceString(String, Chr($a), " ", #PB_String_InPlace)
      ReplaceString(String, Chr($d), " ", #PB_String_InPlace)
      String = FormatDate("[%dd/%mm/%yyyy %hh:%ii:%ss]  ", Date())+ String
    EndIf
    WriteStringN(#LogFileID, String, #PB_UTF8)
    FlushFileBuffers(#LogFileID)
  EndIf
  
  
EndProcedure

Procedure.s wordform(num.i, form_one.s, form_two.s, form_three.s) 
  num % 100 
  Protected num_two.i = num % 10 
  If num > 10 And num < 20 
    ProcedureReturn form_three 
  EndIf 
  If num_two > 1 And num_two < 5 
    ProcedureReturn form_two 
  EndIf 
  If num_two = 1
    ProcedureReturn form_one 
  EndIf 
  ProcedureReturn form_three 
EndProcedure

Procedure.s SN_Text(*x.OW_SN)
  Protected String.s, i
  
  String = RSet(Hex(*x\Type), 2, "0")+"-"
  
  For i=5 To 0 Step -1
    String + RSet(Hex(*x\Number[i]), 2, "0")
  Next i
  
  String + "-"+RSet(Hex(*x\CRC), 2, "0")
  
  ProcedureReturn String
EndProcedure

Procedure UpdateTableAll(List Devices.DevicesInfo())
  Protected String.s, i=0, Err
  
  ClearGadgetItems(#Table)
  ForEach Devices()
    Select Devices()\SN\Type
      Case $10
        String="DS1820 / DS18S20"
      Case $28
        String="DS18B20"
      Default
        String="Неизвестное устройство"
    EndSelect
    
    Err=#False
    ;Devices()\SN\crc+2
    String + Chr(10)+SN_Text(@Devices()\SN)
    If OW_GetCRC(Devices()\SN, 7) <> Devices()\SN\CRC
      String+"  ОШИБКА CRC" : Err=#True
      Devices()\ErrFlag | #DI_ErrFlag_CRC_ROM
    EndIf
    
    String+Chr(10)
    
    If OW_SelectROM(Devices()\SN)
      If OW_ReadPowerSupply()
        String+"3 вывода"
      Else
        String+"2 вывода ('паразитное' питание)"
      EndIf
    Else
      String+"Ошибка"
    EndIf
    
    If Devices()\SN\Type=$10 Or Devices()\SN\Type=$28
      String+Chr(10)+"Измерение"+Chr(10)+"Инициализация"
    EndIf
    AddGadgetItem(#Table, i, String)
    If Err=#True
      SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, #Table_Color_Err, #Table_SN)
    EndIf
    i+1
  Next
  
EndProcedure

Procedure SearchSensor(List Devices.DevicesInfo(), Port.s)
  Protected Sensor.OW_SN, x=0
  
  StatusBarText(#StatusBar_Main, #StatusBar_Main_Status, "Поиск '1-Wire' устройств...")
    
  If ListSize(Devices())>0
    ClearList(Devices())
  EndIf
    
  OW_SearchROM_Reset()
  
  
  While OW_SearchROM_Enum(@Sensor)=#True
    x+1
    If AddElement(Devices())
      CopyStructure(@Sensor, @Devices()\SN, OW_SN)
    EndIf
  Wend
    
  If x>0 
    UpdateTableAll(Devices())
    StatusBarText(#StatusBar_Main, #StatusBar_Main_Status,
                  "Найдено "+x+" '1-Wire' "+wordform(x, "устройство", " устройства", " устройств")+".")
  Else
    ClearGadgetItems(#Table)
    StatusBarText(#StatusBar_Main, #StatusBar_Main_Status,
                  "На порту "+Port+", '1-Wire' устройства не найдены.")
  EndIf
EndProcedure

Procedure SelectPort(List Devices.DevicesInfo())
  Protected Port.s
  
  Port = GetGadgetText(#ComboPort)
  
  OW_ClosePort()
  
  If Port<>""
    If OW_OpenPort(Port)
      StatusBarText(#StatusBar_Main, #StatusBar_Main_Status, "Порт открыт")
    Else
      StatusBarText(#StatusBar_Main, #StatusBar_Main_Status,
                    "Не удалось открыть порт '"+Port+
                    "'  Возможно он зянят или отстуствует в системе")
    EndIf
  EndIf
  
  ClearList(Devices())
  
  If OW_GetLastError() = #OW_Err_OK        ; Ошибок нет.
    SearchSensor(Devices(), Port)
  EndIf
  
EndProcedure


Procedure Thread_OW(*Bool)
  Protected NewList Devices.DevicesInfo()
  Protected String.s, Count, i, Err, OldPos, z
  Protected x.w, Thermo.f, RAM.OW_RAM
  Protected CountErr, OldCountErr
  Protected WarnCount, OldWarnCount
  
  Repeat
    
    Delay(100)
    
    If gOW_Thread\PortChange
      SelectPort(Devices())
      Count = ListSize(Devices())
      If Count>0
        ResetList(Devices())
        i=0
        OldPos=0
      EndIf
      gOW_Thread\PortChange = #False
    EndIf
    
    If Count>0
      
      Repeat
        
        If NextElement(Devices())=0
          i=0
          If gOW_Thread\ConvertOnly=0 ; Раздельный опрос отключен
            ResetList(Devices())
            Break
          Else                        ; Раздельный опрос включен
            If FirstElement(Devices())=0
              Break
            EndIf
          EndIf
        EndIf
        
        
        
        If gOW_Thread\ConvertOnly=0 ; Раздельный опрос отключен
          
          If i=0
            If OW_SkipROM()=#False
              String = GetLastErrorString(OW_GetLastError())
              StatusBarText(#StatusBar_Main, #StatusBar_Main_Status, String)
              Err=#True
              Goto M1
            EndIf
          EndIf
          
        Else                        ; Раздельный опрос включен
          
          If OW_SelectROM(@Devices()\SN)=#False
            String = GetLastErrorString(OW_GetLastError())
            StatusBarText(#StatusBar_Main, #StatusBar_Main_Status, String)
            Err=#True
            Goto M1
          EndIf
          
        EndIf
        
        If i=0 Or gOW_Thread\ConvertOnly
          If OW_ConvertT()=#False
            String = GetLastErrorString(OW_GetLastError())
            StatusBarText(#StatusBar_Main, #StatusBar_Main_Status, String)
            Err=#True
            Goto M1
          EndIf
          
          CountErr=0
          WarnCount=0
          
          
          For z=1 To 8 ; Ждем окончания измерения температуры.
            Delay(100)
            If gOW_Thread\PortChange
              Break
            ElseIf gOW_Thread\BreakTh
              Break 2
            EndIf
          Next z
          
        EndIf
        
        
        
        If Devices()\SN\Type = $10 Or ; DS1820 / DS18S20
           Devices()\SN\Type = $28    ; DS18B20
          
          If OW_SelectROM(@Devices()\SN)
            If OW_ReadRAM(@RAM)
              x=(RAM\H_Termo<<8)|RAM\L_Termo
              If Devices()\SN\Type = $28 ; DS18B20
                Thermo=x/16
              Else
                Thermo=x/2
              EndIf
              Devices()\Thermo = Thermo
              SetGadgetItemText(#Table, i, StrF(Thermo, 1)+" °C", #Table_Thermo)
              
              If gOW_Thread\Log
                WriteLog(SN_Text(@Devices()\SN)+"  —  "+StrF(Thermo, 1)+" °C")
              EndIf
              
              
              If Thermo<-55 Or Thermo>127
                Devices()\ErrFlag | #DI_ErrFlag_Thermo
                SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, #Table_Color_Warn, #Table_Thermo)
              Else
                Devices()\ErrFlag & ~#DI_ErrFlag_Thermo
                x/16
                If gOW_Thread\ThermoControl=0 Or
                   (gOW_Thread\ThermoMin<=x And x<=gOW_Thread\ThermoMax)
                  SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, -1, #Table_Thermo)
                Else
                  WarnCount+1
                  SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, #Table_Color_Warn, #Table_Thermo)
                EndIf
              EndIf
              If OW_GetCRC(@RAM, 8)=RAM\CRC
                Devices()\ErrFlag & ~#DI_ErrFlag_CRC_RAM
                SetGadgetItemText(#Table, i, "OK", #Table_Status)
                SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, -1, #Table_Status)
              Else
                Devices()\ErrFlag | #DI_ErrFlag_CRC_RAM
                SetGadgetItemText(#Table, i, "Ошибка CRC", #Table_Status)
                SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, #Table_Color_Err, #Table_Status)
              EndIf
            EndIf
          EndIf
          
          If OW_GetLastError()<>#OW_Err_OK 
            Devices()\ErrFlag | #DI_ErrFlag_OW
            SetGadgetItemText(#Table, i, GetLastErrorString(OW_GetLastError()), #Table_Status)
            SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, #Table_Color_Err, #Table_Status)
          ElseIf Devices()\ErrFlag & #DI_ErrFlag_CRC_RAM = 0
            Devices()\ErrFlag & ~#DI_ErrFlag_OW
            SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, -1, #Table_Status)
          EndIf
          
        Else
          Continue
        EndIf
        
        
        
        If Devices()\ErrFlag
          CountErr+1
          If Devices()\ErrFlag & ~#DI_ErrFlag_CRC_ROM = 0
            SetGadgetItemText(#Table, i, "", #Table_Thermo)
          EndIf
        EndIf
        
        If gOW_Thread\ConvertOnly ; Раздельный опрос включен
          SetGadgetItemColor(#table, OldPos, #PB_Gadget_BackColor, -1, #Table_Type)
          SetGadgetItemColor(#table, i, #PB_Gadget_BackColor, RGB(128, 255, 128), #Table_Type)
        EndIf
        
        OldPos=i
        i+1
        
        If gOW_Thread\ConvertOnly ; Раздельный опрос включен
          Break
        EndIf
        
      ForEver
      
      
      If Err=#True Or CountErr<>OldCountErr Or OldWarnCount<>WarnCount
        
        If CountErr>0
          String=".    "+wordform(CountErr, "Обнаружен ", "Обнаружено ", "Обнаружены ")+
                 CountErr+wordform(CountErr, " датчик", " датчика", " датчиков")+
                 " с ошибками в работе"
        Else
          String="."
        EndIf
        
        If WarnCount>0
          String+"  В "+WarnCount+wordform(WarnCount, " датчике", " датчиках", " датчиках")+
                 " зафиксирован выход за пределы допустимой температуры"
        EndIf
        
        StatusBarText(#StatusBar_Main, #StatusBar_Main_Status,
                      "Найдено "+Count+" '1-Wire' "+
                      wordform(Count, "устройство", " устройства", " устройств")+String)
        Err=#False
        OldCountErr = CountErr
        OldWarnCount=WarnCount
      EndIf
      
      
      If gOW_Thread\ThermoInterval>1
        For z=1 To gOW_Thread\ThermoInterval-1
          For x=1 To 10
            Delay(100)
            If gOW_Thread\PortChange Or gOW_Thread\BreakTh
              Break 2
            EndIf
          Next x
        Next z
      EndIf
     
      
      
    Else
      
      M1:
      If WarnCount<>-1
        x=CountGadgetItems(#Table)-1
        For i=0 To x
          SetGadgetItemText(#Table, i, "", #Table_Thermo)
          SetGadgetItemText(#Table, i, String, #Table_Status)
          SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, -1, #Table_Thermo)
          SetGadgetItemColor(#Table, i, #PB_Gadget_BackColor, #Table_Color_Err, #Table_Status)
        Next
        WarnCount=-1
      EndIf
      
      CountErr+1
      If CountErr>=10
        Count=0
        If OW_Reset()
          SearchSensor(Devices(), GetGadgetText(#ComboPort))
          Count = ListSize(Devices())
        EndIf
        CountErr=0
      EndIf
      
      
    EndIf
    
  Until gOW_Thread\BreakTh
  
EndProcedure

Procedure EnumPorts(Gadget)
  Protected i, ID
  
  ClearGadgetItems(Gadget)
  
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    AddGadgetItem(Gadget, -1, "/dev/tty.usbserial")
  CompilerElse
    
    For i=0 To 10
      ID = OpenSerialPort(#PB_Any, #PortName+i, 9600, #PB_SerialPort_NoParity,
                          8, 1, #PB_SerialPort_NoHandshake, 255, 255)
      If ID
        AddGadgetItem(Gadget, -1, #PortName+i)
        CloseSerialPort(ID)
      EndIf
    Next
    
  CompilerEndIf

EndProcedure

Procedure OpenWinMain()
  Protected x
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Linux
    SetGadgetFont(#PB_Default, FontID(LoadFont(#PB_Any,"Liberation Mono",10)))
  CompilerEndIf
  
  OpenWindow(#WinMain, 0, 0, 590, 400, "DS18B20 термо-мониторинг v1.1", 
             #PB_Window_MinimizeGadget|#PB_Window_Invisible|#PB_Window_ScreenCentered)
  
  CreateMenu(#Menu_Main, WindowID(#WinMain))
  MenuTitle("Файл")
    MenuItem(#MItem_Log, "Записывать в лог-файл")
    MenuBar()
    MenuItem(#MItem_Exit, "Выход")
  MenuTitle("Режим")
    MenuItem(#MItem_Mode_CPU_MinUsage, "Снизить нагрузку на CPU")
    MenuItem(#MItem_Mode_Invert, "Инвертировать DTR и RTS")
    MenuItem(#MItem_ConvertOnly, "Раздельный опрос")
  MenuTitle("Справка")
    MenuItem(#MItem_About, "О программе")
  
  x=TextGadget(#PB_Any, 4, 8, 100, 16, "Порт:")
  ResizeGadget(x, #PB_Ignore, #PB_Ignore, GadgetWidth(x, #PB_Gadget_RequiredSize)+4, #PB_Ignore)
  
  ComboBoxGadget(#ComboPort, GadgetX(x)+GadgetWidth(x), 4, 100, 22)
  EnumPorts(#ComboPort)
  
  
  x=TextGadget(#PB_Any, GadgetX(#ComboPort)+GadgetWidth(#ComboPort)+10, 8, 350, 16, "Интервал опроса:")
  ResizeGadget(x, #PB_Ignore, #PB_Ignore, GadgetWidth(x, #PB_Gadget_RequiredSize)+4, #PB_Ignore)
  
  SpinGadget(#SpinThermoInterval, GadgetX(x)+GadgetWidth(x), 4, 50, 22, 1, 60,
             #PB_Spin_ReadOnly|#PB_Spin_Numeric)
  GadgetToolTip(#SpinThermoInterval, "Интервал в секундах опроса датчиков")
  
  CheckBoxGadget(#CheckThermoControl, GadgetX(#SpinThermoInterval)+GadgetWidth(#SpinThermoInterval)+16,
                 8, 100, 16, "Термо контроль")
  ResizeGadget(#CheckThermoControl, #PB_Ignore, #PB_Ignore,
               GadgetWidth(#CheckThermoControl, #PB_Gadget_RequiredSize)+2, #PB_Ignore)
  
;   CheckBoxGadget(#CheckConvertOnly, GadgetX(#CheckThermoControl),
;                  GadgetY(#CheckThermoControl)+GadgetHeight(#CheckThermoControl)+2, 100, 16, )
;   ResizeGadget(#CheckConvertOnly, #PB_Ignore, #PB_Ignore,
;                GadgetWidth(#CheckConvertOnly, #PB_Gadget_RequiredSize)+2, #PB_Ignore)
;   GadgetToolTip(#CheckConvertOnly, "Опрос датчиков по одному. Необходимо для работы с множеством датчиков с 'паразитным' питанием. Внимание! Скорость считывания уменьшается по мере увеличения количества датчиков")
  
  ContainerGadget(#ContainerThermoControl, GadgetX(#CheckThermoControl)+GadgetWidth(#CheckThermoControl),
                  2, 200, 28, #PB_Container_Single)
  
  x=TextGadget(#PB_Any, 4, 7, 350, 16, "Мин:")
  ResizeGadget(x, #PB_Ignore, #PB_Ignore, GadgetWidth(x, #PB_Gadget_RequiredSize)+4, #PB_Ignore)
  
  SpinGadget(#SpinThermoMin, GadgetX(x)+GadgetWidth(x), 2, 50, 22, -55, 125,
             #PB_Spin_ReadOnly|#PB_Spin_Numeric)
  GadgetToolTip(#SpinThermoMin, "Минимальная допустимая температура")
  
  x=TextGadget(#PB_Any, GadgetX(#SpinThermoMin)+GadgetWidth(#SpinThermoMin)+10, 7, 350, 16, "Макс:")
  ResizeGadget(x, #PB_Ignore, #PB_Ignore, GadgetWidth(x, #PB_Gadget_RequiredSize)+4, #PB_Ignore)
  
  SpinGadget(#SpinThermoMax, GadgetX(x)+GadgetWidth(x), 2, 50, 22, -55, 125,
             #PB_Spin_ReadOnly|#PB_Spin_Numeric)
  GadgetToolTip(#SpinThermoMax, "Максимальная допустимая температура")
  
  CloseGadgetList()
  
  ResizeGadget(#ContainerThermoControl, #PB_Ignore, #PB_Ignore, 
               GadgetX(#SpinThermoMax)+GadgetWidth(#SpinThermoMax)+10, #PB_Ignore)
  
  ResizeWindow(#WinMain, #PB_Ignore, #PB_Ignore, 
               GadgetX(#ContainerThermoControl)+GadgetWidth(#ContainerThermoControl)+2, #PB_Ignore)
  
  x=ListIconGadget(#Table, 2, GadgetY(#ContainerThermoControl)+GadgetHeight(#ContainerThermoControl)+2, 
                   WindowWidth(#WinMain)-4, 304, "Тип", 80, 
                   #PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection)
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    SendMessage_(x, #LVM_SETEXTENDEDLISTVIEWSTYLE, 
                 #LVS_EX_LABELTIP|#LVS_EX_GRIDLINES|#LVS_EX_DOUBLEBUFFER, 
                 #LVS_EX_LABELTIP|#LVS_EX_GRIDLINES|#LVS_EX_DOUBLEBUFFER)
  CompilerEndIf
  
  AddGadgetColumn(#Table, #Table_SN, "Серийный номер", 150)
  AddGadgetColumn(#Table, #Table_Power, "Подключение", 100)
  AddGadgetColumn(#Table, #Table_Thermo, "Температура", 100)
  AddGadgetColumn(#Table, #Table_Status, "Статус", 120)
  
  CreateStatusBar(#StatusBar_Main, WindowID(#WinMain))
  AddStatusBarField(#PB_Ignore)
  
  ResizeGadget(#Table, #PB_Ignore, #PB_Ignore, #PB_Ignore,
               WindowHeight(#WinMain)-StatusBarHeight(#StatusBar_Main)-GadgetY(#Table)-MenuHeight()-2)
  
  HideWindow(#WinMain, #False)
EndProcedure

Procedure ReadSetting()
  Protected String.s, x,Flag, p 
  
  x=OpenPreferences("ThermoMon.ini")
  
  String=ReadPreferenceString("Port", "")
  If String
    SetGadgetText(#ComboPort, String)
  Else
    SetGadgetState(#ComboPort, -1)
  EndIf
  gOW_Thread\ThermoInterval = ReadPreferenceLong("Interval", 1)
  If gOW_Thread\ThermoInterval<=0 Or gOW_Thread\ThermoInterval>100
    gOW_Thread\ThermoInterval=1
  EndIf
  SetGadgetState(#SpinThermoInterval, gOW_Thread\ThermoInterval)
  SetGadgetText(#SpinThermoInterval, Str(gOW_Thread\ThermoInterval))
  
  gOW_Thread\ThermoControl = ReadPreferenceLong("ThermoControl", 0)&1
  SetGadgetState(#CheckThermoControl, gOW_Thread\ThermoControl)
  
  gOW_Thread\ConvertOnly = ReadPreferenceLong("ConvertOnly", 0)&1
  SetMenuItemState(#Menu_Main, #MItem_ConvertOnly, gOW_Thread\ConvertOnly)
  
  gOW_Thread\ThermoMin = ReadPreferenceLong("ThermoMin", 0)
  gOW_Thread\ThermoMax = ReadPreferenceLong("ThermoMax", 10)
  
  gOW_Thread\Log = ReadPreferenceLong("Log", 0)&1
  SetMenuItemState(#Menu_Main, #MItem_Log, gOW_Thread\Log)
  
  Flag = OW_GetMode()
  p = ReadPreferenceLong("CPU_MinUsage", 0)&1
  If p
    Flag|#OW_Mode_CPU_MinUsage
  EndIf
  SetMenuItemState(#Menu_Main, #MItem_Mode_CPU_MinUsage, p)
  
  p=ReadPreferenceLong("Invert", 0)
  If p
    Flag|#OW_Mode_Invert
  EndIf
  SetMenuItemState(#Menu_Main, #MItem_Mode_Invert, p)
  
  OW_SetMode(Flag)                 
  
  If gOW_Thread\ThermoMin<=-55 Or gOW_Thread\ThermoMin>130
    gOW_Thread\ThermoMin=0
  EndIf
  
  If gOW_Thread\ThermoMax<=-55 Or gOW_Thread\ThermoMax>130
    gOW_Thread\ThermoMax=10
  EndIf
  
  If gOW_Thread\ThermoMin>=gOW_Thread\ThermoMax
    gOW_Thread\ThermoMin=gOW_Thread\ThermoMax-1
  EndIf
  
  SetGadgetState(#SpinThermoMin, gOW_Thread\ThermoMin)
  SetGadgetText(#SpinThermoMin, Str(gOW_Thread\ThermoMin))
   
  
  SetGadgetState(#SpinThermoMax, gOW_Thread\ThermoMax)
  SetGadgetText(#SpinThermoMax, Str(gOW_Thread\ThermoMax))
  
  PostEvent(#PB_Event_Gadget, #WinMain, #ComboPort, #PB_EventType_Change)
  PostEvent(#PB_Event_Gadget, #WinMain, #CheckThermoControl)
  
  If x
    ClosePreferences()
  EndIf
EndProcedure

Procedure WriteSetting()
  If CreatePreferences("ThermoMon.ini")
    
    WritePreferenceString("Port", GetGadgetText(#ComboPort))
    WritePreferenceLong("Interval", GetGadgetState(#SpinThermoInterval))
    WritePreferenceLong("ThermoControl", GetGadgetState(#CheckThermoControl))
    WritePreferenceLong("ConvertOnly", GetMenuItemState(#Menu_Main, #MItem_ConvertOnly))
    WritePreferenceLong("ThermoMin", GetGadgetState(#SpinThermoMin))
    WritePreferenceLong("ThermoMax", GetGadgetState(#SpinThermoMax))
    WritePreferenceLong("Log", gOW_Thread\Log)
    WritePreferenceLong("CPU_MinUsage", GetMenuItemState(#Menu_Main, #MItem_Mode_CPU_MinUsage))
    WritePreferenceLong("Invert", GetMenuItemState(#Menu_Main, #MItem_Mode_Invert))
    
    ClosePreferences()
  EndIf
EndProcedure


Procedure PortChange()
  gOW_Thread\PortChange = #True
  WriteSetting()
EndProcedure

SetCurrentDirectory(GetPathPart(ProgramFilename()))

OpenWinMain()
BindGadgetEvent(#ComboPort, @PortChange(), #PB_EventType_Change)

ReadSetting()

Thread_OW_ID=CreateThread(@Thread_OW(), 0)

Repeat
  Event = WaitWindowEvent()
  Window = EventWindow()
  
  If Window = #WinMain
    If Event = #PB_Event_Menu
      Select EventMenu()
        Case #MItem_Log
          gOW_Thread\Log = GetMenuItemState(#Menu_Main, #MItem_Log)!1
          SetMenuItemState(#Menu_Main, #MItem_Log, gOW_Thread\Log)
          WriteSetting()
        Case #MItem_Exit : Break
        Case #MItem_Mode_CPU_MinUsage
          x=GetMenuItemState(#Menu_Main, #MItem_Mode_CPU_MinUsage)!1
          SetMenuItemState(#Menu_Main, #MItem_Mode_CPU_MinUsage, x)
          If x
            OW_SetMode(OW_GetMode()|#OW_Mode_CPU_MinUsage)
          Else
            OW_SetMode(OW_GetMode()&~#OW_Mode_CPU_MinUsage)
          EndIf
        Case #MItem_Mode_Invert
          x=GetMenuItemState(#Menu_Main, #MItem_Mode_Invert)!1
          SetMenuItemState(#Menu_Main, #MItem_Mode_Invert, x)
          If x
            OW_SetMode(OW_GetMode()|#OW_Mode_Invert)
          Else
            OW_SetMode(OW_GetMode()&~#OW_Mode_Invert)
          EndIf
        Case #MItem_ConvertOnly
          x = GetMenuItemState(#Menu_Main, #MItem_ConvertOnly) ! 1
          gOW_Thread\ConvertOnly = x
          SetMenuItemState(#Menu_Main, #MItem_ConvertOnly, x)
          If x=0
            SetGadgetItemColor(#table, -1, #PB_Gadget_BackColor, -1, #Table_Type)
          EndIf
        Case #MItem_About
          MessageRequester("О программе", "Мониторинг температуры с использованием DS18x20."+Chr(10)+
                                          "E-Mail: pure-basic@yandex.ru")
      EndSelect
      
    ElseIf Event = #PB_Event_Gadget
      Select EventGadget()
        Case #SpinThermoInterval
          gOW_Thread\ThermoInterval = GetGadgetState(#SpinThermoInterval)
          WriteSetting()
        Case #CheckThermoControl
          gOW_Thread\ThermoControl = GetGadgetState(#CheckThermoControl)
          WriteSetting()          
        Case #SpinThermoMin
          x=GetGadgetState(#SpinThermoMin)
          Select EventType()
            Case #PB_EventType_Change
              gOW_Thread\ThermoMin = x
              WriteSetting()
            Case #PB_EventType_Up
              If x>=GetGadgetState(#SpinThermoMax)
                x=GetGadgetState(#SpinThermoMax)-1
                SetGadgetState(#SpinThermoMin, x)
                SetGadgetText(#SpinThermoMin, Str(x))
              EndIf
          EndSelect
        Case #SpinThermoMax
          x=GetGadgetState(#SpinThermoMax)
          Select EventType()
            Case #PB_EventType_Change
              gOW_Thread\ThermoMax = x
              WriteSetting()
            Case #PB_EventType_Down
              If x=<GetGadgetState(#SpinThermoMin)
                x=GetGadgetState(#SpinThermoMin)+1
                SetGadgetState(#SpinThermoMax, x)
                SetGadgetText(#SpinThermoMax, Str(x))
              EndIf
          EndSelect
      EndSelect
    EndIf
  EndIf
  
Until Event = #PB_Event_CloseWindow And Window = #WinMain

gOW_Thread\BreakTh=#True
HideWindow(#WinMain, #True)
WriteSetting()
WaitThread(Thread_OW_ID, 400)
If IsThread(Thread_OW_ID)
  KillThread(Thread_OW_ID)
EndIf

If IsFile(#LogFileID)
  CloseFile(#LogFileID)
EndIf

End
Screenshot.
Image

If you use a USB to COM adapter like this (chip PL2303).
Image

scheme is very simple.
Image