DS18B20 Module

Share your advanced PureBasic knowledge/code with the community.
User_Russian
Addict
Addict
Posts: 1520
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

DS18B20 Module

Post 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.
User avatar
Kukulkan
Addict
Addict
Posts: 1396
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: DS18B20 Module

Post by Kukulkan »

Great stuff! Thank you! :D
User avatar
minimy
Enthusiast
Enthusiast
Posts: 560
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Re: DS18B20 Module

Post by minimy »

Really amazing. Thank you very much friend!
And merry christmas!

Большое спасибо друг! :wink:
If translation=Error: reply="Sorry, Im Spanish": Endif
dige
Addict
Addict
Posts: 1391
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: DS18B20 Module

Post by dige »

Thank you User_Russian!
Very nice, but could you please provide some more informations about the serial adapter?
"Daddy, I'll run faster, then it is not so far..."
User_Russian
Addict
Addict
Posts: 1520
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: DS18B20 Module

Post 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.
Stephane Fonteyne
User
User
Posts: 27
Joined: Sun Sep 06, 2015 2:22 pm

Re: DS18B20 Module

Post 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
User_Russian
Addict
Addict
Posts: 1520
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: DS18B20 Module

Post 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
ElementE
Enthusiast
Enthusiast
Posts: 139
Joined: Sun Feb 22, 2015 2:33 am

Re: DS18B20 Module

Post 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.
Think Unicode!
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Re: DS18B20 Module

Post 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". :)
I like logic, hence I dislike humans but love computers.
ElementE
Enthusiast
Enthusiast
Posts: 139
Joined: Sun Feb 22, 2015 2:33 am

Re: DS18B20 Module

Post 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?
Think Unicode!
User_Russian
Addict
Addict
Posts: 1520
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: DS18B20 Module

Post by User_Russian »

In this word spelling error. Must be "занят". In English this "busy".
ElementE
Enthusiast
Enthusiast
Posts: 139
Joined: Sun Feb 22, 2015 2:33 am

Re: DS18B20 Module

Post by ElementE »

Thank you User_Russian.
Think Unicode!
ElementE
Enthusiast
Enthusiast
Posts: 139
Joined: Sun Feb 22, 2015 2:33 am

Re: DS18B20 Module

Post 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
Think Unicode!
ElementE
Enthusiast
Enthusiast
Posts: 139
Joined: Sun Feb 22, 2015 2:33 am

Re: DS18B20 Module

Post 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
Think Unicode!
User_Russian
Addict
Addict
Posts: 1520
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: DS18B20 Module

Post 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
Post Reply