Код:
Global Baud.l,Data_bit.l,Stop_bit.f,Parity_status.l,Dim DataRTU.a(7),StringASCII.s,Block.a,Text_In.s="",
ResponseTimeInt.a,Port.s,Ctr1.LARGE_INTEGER,Ctr2.LARGE_INTEGER,Freq.LARGE_INTEGER

Procedure LRC16()
  Protected lrc.a, i.a, Dim DataASCII.a(7)
  ;создаём массив данных для передачи:
  DataASCII(0) = Val("$" + GetGadgetText(11))   ;адрес
  DataASCII(1) = Val("$" + GetGadgetText(12))   ;команда 
  DataASCII(2) = Val("$" + GetGadgetText(13))   ;старший байт начального регистра 
  DataASCII(3) = Val("$" + GetGadgetText(14))   ;младший байт начального регистра
  DataASCII(4) = Val("$" + GetGadgetText(15))   ;старший байт количества регистров
  DataASCII(5) = Val("$" + GetGadgetText(16))   ;младший байт количества регистров  
    lrc = 0   ;подсчитываем LRC
  For i = 0 To 5     
    lrc = lrc + DataASCII(i)
  Next i
     lrc = 255 - lrc + 1
     StringASCII.s=""
  For i = 11 To 16 
    If (Len(GetGadgetText(i))=2)
      StringASCII = StringASCII + GetGadgetText(i)
    Else 
      StringASCII =  StringASCII + "0" + GetGadgetText(i)
    EndIf
  Next i  
     StringASCII = Chr(58) + StringASCII + Hex(lrc) + Chr(13) + Chr(10)
EndProcedure

Procedure CRC16()
  Protected crc.u, i.a, j.a 
  ;создаём массив данных для передачи:
  DataRTU(0) = Val("$" + GetGadgetText(11))   ;адрес
  DataRTU(1) = Val("$" + GetGadgetText(12))   ;команда 
  DataRTU(2) = Val("$" + GetGadgetText(13))   ;старший байт начального регистра
  DataRTU(3) = Val("$" + GetGadgetText(14))   ;младший байт начального регистра
  DataRTU(4) = Val("$" + GetGadgetText(15))   ;старший байт количества регистров
  DataRTU(5) = Val("$" + GetGadgetText(16))   ;младший байт количества регистров
    crc = 65535    ;подсчитываем CRC
  For i = 0 To 5
    crc = crc ! DataRTU(i)
    For j = 1 To 8
      If ((crc & 1) <> 0) 
        crc = (crc / 2)
        crc = (crc ! 40961)
      Else
        crc = (crc / 2)
      EndIf
    Next j
  Next i
  crc = crc & 65535
  DataRTU(7) = crc / 256              ;старший байт CRC
  DataRTU(6) = crc - (DataRTU(7) * 256) ;младший байт CRC
EndProcedure

Procedure Select_ComPort()      
  Port = GetGadgetText(3)
      If IsSerialPort(2)   ; Если с таким ИД уже открыт порт.      
           CloseSerialPort(2) ; то закрываем его.
           SetGadgetColor(5,#PB_Gadget_BackColor, $0000FF)
           SetGadgetText(17,"Открыть порт")
           AddGadgetItem(4, -1, "Порт закрыт.")
      Else
           OpenSerialPort(2, Port, Baud, Parity_status, Data_bit, Stop_bit, #PB_SerialPort_NoHandshake, 1, 1) 
           If IsSerialPort(2)
               SetGadgetColor(5,#PB_Gadget_BackColor, $00FF00)
               SetGadgetText(17,"Закрыть порт")
               AddGadgetItem(4, -1, "Порт открыт.")             
           Else
               MessageRequester("Ошибка","Выбранный порт недоступен!",16);Выводим сообщение об ошибке. 
           EndIf 
      EndIf           
EndProcedure      

Procedure InData() ; Прием данных.      
  Protected InBytes.a, Temp_Text.s
   If IsSerialPort(2)   ; если с таким ИД порт открыт.      
    InBytes = AvailableSerialPortInput(2) ;получаем число принятых байт     
    If InBytes>0                          ; если получены данные 
        Protected Dim InBuffer.a(InBytes); создаём массив соотв. размера
      RealInBytes = ReadSerialPortData(2, @InBuffer(), InBytes);переписываем в него
       If RealInBytes>0 
         If AddWindowTimer(0,20,100); если 0,1 сек таймер был запущен
           RemoveWindowTimer(0,20)  ; то обнуляем его
         EndIf
           AddWindowTimer(0,20,100);  запускаем 0,1 сек таймер
            If QueryPerformanceFrequency_(Freq)
              QueryPerformanceCounter_(Ctr2)    ;окончание отсчёта времени ответа
              ResponseTime.f=(Ctr2\lowpart - Ctr1\lowpart )*1000/ Freq\lowpart  ;измеренное время в миллисекундах
              ResponseTimeInt=Round(ResponseTime,#PB_Round_Up)
            EndIf
            For i=0 To (InBytes-1);составляем строку для записи в окно
             Temp_Text = Hex(InBuffer(i))
               If Len(Temp_Text) = 2
               Else 
                 Temp_Text = "0" + Temp_Text
               EndIf
             Text_In = Text_In + Temp_Text + " "
            Next i      
       EndIf 
    EndIf      
   EndIf      
EndProcedure      
  
Procedure Out_RTU()  ; Передача данных в порт
  Protected Text_Out.s="", Temp_Text.s  
     If IsSerialPort(2) ;если порт открыт          
         WriteSerialPortData(2,@DataRTU(),8) ;передаём 
           For i=0 To 7          ;составляем строку для записи в окно
             Temp_Text=Hex((DataRTU(i)))       
              If Len(Temp_Text)=2
              Else 
                Temp_Text="0" + Temp_Text
              EndIf
             Text_Out.s=Text_Out.s + Temp_Text + " "
           Next i  
         AddGadgetItem(4, -1, "Отправлен запрос:")
         AddGadgetItem(4, -1, Text_Out)    ;переписываем в окно        
     Else  
         MessageRequester("Ошибка","Текущий порт недоступен!",16);Выводим сообщение об ошибке. 
     EndIf 
EndProcedure   
   
 Procedure Out_ASCII()      ; Передача данных в порт
  Protected Text_Out.s="", Temp_Text.s  
     If IsSerialPort(2)     ;если порт открыт          
       WriteSerialPortString(2,StringASCII,1)  ;передаём
         For i=1 To 17      
           Temp_Text=Hex(Asc(Mid(StringASCII,i,1))) 
              If Len(Temp_Text) = 2
              Else 
                 Temp_Text = "0" + Temp_Text
              EndIf
                 Text_Out=Text_Out+Temp_Text+" "
         Next i 
       AddGadgetItem(4, -1, "Отправлен запрос:")
       AddGadgetItem(4, -1, Text_Out)         
     Else  
       MessageRequester("Ошибка","Текущий порт недоступен!",16);Выводим сообщение об ошибке. 
     EndIf 
EndProcedure 

 Procedure IsMouseOver(wnd) ;Определяем,находится ли курсор мышки в пределах данного окна  
   GetWindowRect_(wnd,re.RECT)  
   GetCursorPos_(pt.POINT)  
   ProcedureReturn PtInRect_(@re, pt\x|(pt\y<<32))  
 EndProcedure 

If OpenWindow(0, 0, 0, 720, 400, "Modbus Tester.  Бесплатное ПО.", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)
     ButtonGadget(1, 240, 180, 80, 20, "Запрос")  
     ComboBoxGadget(2, 30, 40, 70, 20) 
         AddGadgetItem(2,-1,"RTU")
         AddGadgetItem(2,-1,"ASCII")
         SetGadgetState(2,0)
         GadgetToolTip(2,"Протокол");  
     ComboBoxGadget(3, 40, 180, 70, 20) ;Выбор СОМ порта       
           For i=1 To 99          
             AddGadgetItem(3,-1,"COM" + Str(i))          
           Next i              
        SetGadgetState(3,0); Делаем активным нулевой пункт выпадающего списка.                       
     EditorGadget(4, 350, 40, 360, 350)  ;окно сообщений 
         GadgetToolTip(4,"Используется .hex формат представления чисел")
     TextGadget(5,110, 180, 20, 20, "  ")     ;Здесь будет отображаться результат открытия порта        
     ButtonGadget(6, 90, 250, 90, 20, "Очистить окно")  ;очистить окно сообщений         
     ComboBoxGadget(7, 110, 40, 65, 20) 
         AddGadgetItem(7,-1,"1200")
         AddGadgetItem(7,-1,"1800")
         AddGadgetItem(7,-1,"2400")
         AddGadgetItem(7,-1,"4800")
         AddGadgetItem(7,-1,"7200")  
         AddGadgetItem(7,-1,"9600")
         AddGadgetItem(7,-1,"14400")
         AddGadgetItem(7,-1,"19200")
         AddGadgetItem(7,-1,"38400")
         AddGadgetItem(7,-1,"57600")
         AddGadgetItem(7,-1,"115200")
         AddGadgetItem(7,-1,"128000") 
         SetGadgetState(7,5);
         GadgetToolTip(7,"Скорость");
     ComboBoxGadget(8, 190, 40, 40, 20, #PB_String_UpperCase)
         AddGadgetItem(8,-1,"N")
         AddGadgetItem(8,-1,"E")
         AddGadgetItem(8,-1,"M")
         AddGadgetItem(8,-1,"O")
         AddGadgetItem(8,-1,"S")
         SetGadgetState(8,0);
         GadgetToolTip(8,"Чётность");
     ComboBoxGadget(9, 240, 40, 40, 20)
         AddGadgetItem(9,-1,"5")
         AddGadgetItem(9,-1,"6")
         AddGadgetItem(9,-1,"7")
         AddGadgetItem(9,-1,"8") 
         SetGadgetState(9,3);
         GadgetToolTip(9,"Количество бит данных");
     ComboBoxGadget(10, 290, 40, 40, 20) 
         AddGadgetItem(10,-1,"1")
         AddGadgetItem(10,-1,"1.5")
         AddGadgetItem(10,-1,"2")        
         SetGadgetState(10,0); 
         GadgetToolTip(10,"Количество стоповых бит")
     StringGadget(11, 90, 110, 30, 20, "00", #PB_String_UpperCase) 
         GadgetToolTip(11,"Адрес.hex")         
     StringGadget(12, 120, 110, 30, 20, "00", #PB_String_UpperCase)
         GadgetToolTip(12,"Команда.hex")
     StringGadget(13, 150, 110, 30, 20, "00", #PB_String_UpperCase)
         GadgetToolTip(13,"Старший байт начального регистра.hex")
     StringGadget(14, 180, 110, 30, 20, "00", #PB_String_UpperCase)
         GadgetToolTip(14,"Младший байт начального регистра.hex")
     StringGadget(15, 210, 110, 30, 20, "00", #PB_String_UpperCase)
         GadgetToolTip(15,"Старший байт количества регистров.hex")
     StringGadget(16, 240, 110, 30, 20, "00", #PB_String_UpperCase)    
         GadgetToolTip(16,"Младший байт количества регистров.hex")
     ButtonGadget(17, 130, 180, 90, 20, "Открыть порт") 
     TextGadget(18,270, 118, 30, 20, ".HEX  ")
     ButtonGadget(19, 200, 250, 80, 20, "Справка")
     
   If CreatePopupMenu(1)  
     MenuItem(1,"Отменить")  
     MenuItem(2,"Повторить")  
     MenuBar()  
     MenuItem(3,"Вырезать")  
     MenuItem(4,"Копировать")  
     MenuItem(5,"Вставить")  
     MenuBar()  
     MenuItem(6,"Выделить всё")  
  EndIf    
     
  Repeat 

          Baud=Val(GetGadgetText(7))      ;выбираем скорость передачи
          Data_bit=Val(GetGadgetText(9))  ;выбираем число бит данных
          Stop_bit=Val(GetGadgetText(10)) ;выбираем число стоповых бит
       
          If ((GetGadgetText(8))="N")     
            Parity_status=0
          ElseIf ((GetGadgetText(8))="E")
            Parity_status=2
          ElseIf ((GetGadgetText(8))="M")
           Parity_status=3
          ElseIf ((GetGadgetText(8))="O")
           Parity_status=1 
          ElseIf ((GetGadgetText(8))="S")
           Parity_status=4 
          EndIf          
          
       InData() ;принимаем данные из UART
          
       Event=WaitWindowEvent()  
       
       If Event=#PB_Event_Timer And EventTimer()=20
         RemoveWindowTimer(0,20)        ; выключаем 0,1с таймер        
         AddGadgetItem(4, -1, ("Через "+Str(ResponseTimeInt)+" миллисекунд получен ответ:"))
         AddGadgetItem(4, -1, Text_In)    ;переписываем в окно
         Text_In=""
         RemoveWindowTimer(0,21)       ; выключаем 1с таймер          
         Block=0                                       ; разблокируем кнопку "Запрос" 
         SetGadgetText(1,"Запрос")          
       EndIf
          
       If Event=#PB_Event_Timer And EventTimer()=21
         RemoveWindowTimer(0,21)                   ; выключаем 1с таймер 
         AddGadgetItem(4, -1, "Ответа нет.")    ;переписываем в окно
         Block=0                                                   ; разблокируем кнопку "Запрос"
         SetGadgetText(1,"Запрос")             
       EndIf 
          
       If Event=#PB_Event_Gadget        
         Gadget=EventGadget ( )       ;узнаём,какой гаджет был активирован        
         If Gadget=1                    ;если нажата кнопка "Запрос"
           If Block                  
           Else                         ;если разблокирована кнопка "Запрос"
              Protocol.s=GetGadgetText(2)
                 Select Protocol    ;выбираем протокол передачи
                    Case "RTU"
                        CRC16()  
                        Out_RTU()      
                    Case "ASCII"
                        LRC16()       
                        Out_ASCII()
                 EndSelect 
                    If QueryPerformanceFrequency_(Freq)
                        QueryPerformanceCounter_(Ctr1)    ;начало отсчёта времени ответа 
                    EndIf 
                      If IsSerialPort(2)
                        AddWindowTimer(0,21,1000); включить 1с таймер
                        AddGadgetItem(4, -1, "Ждём одну секунду.")    ;переписываем в окно                 
                        Block=1; блокируем кнопку "Запрос"
                        SetGadgetText(1,"Ждём...") 
                      EndIf 
                    EndIf 
         ElseIf (Gadget=7 Or  Gadget=8 Or  Gadget=9 Or  Gadget=10);если изменены параметры порта
            If IsSerialPort(2)          
              CloseSerialPort(2) 
              OpenSerialPort(2, Port, Baud, Parity_status, Data_bit, Stop_bit, #PB_SerialPort_NoHandshake, 1, 1) 
            EndIf
         ElseIf Gadget=17                    ;если был активирован выпадающий список          
           ComboBox.s=GetGadgetText(3) ;считываем текст из текущего пункта выпадающего списка.                
           Select_ComPort()                          
         ElseIf Gadget=6        ;если нажата кнопка очистки      
           ClearGadgetItems(4)  ;удаляем содержимое окна с данными
         ElseIf Gadget=19       ;нажата кнопка "Справка"
           MessageRequester("О программе","Данное программное обеспечение распространяется бесплатно, "+Chr(34)+"как есть"+Chr(34)+". "+Chr(13)+"По открытии порта тестер начинает слушать линию. "+Chr(13)+"Принятые данные показываются в окошке. "+Chr(13)+"При передаче в запрос сама добавляется контрольная сумма."+Chr(13)+"Поддерживаются команды 01, 02, 03, 04, 05, 06. "+Chr(13)+"Программа написана на языке Purebasic. "+Chr(13)+"Успехов!   Сёмин ВМ   САРАТОВ   2016")
         EndIf        
       EndIf 
       
       If Event=#WM_RBUTTONUP  
         If IsMouseOver(GadgetID(4))  
           DisplayPopupMenu(1,WindowID(0))  
         EndIf  
       EndIf  
   
       If Event=#PB_Event_Menu  
         Menu=EventMenu()  
         Select Menu  
         Case 1  
           SendMessage_(GadgetID(4), #EM_UNDO, #Null, #Null)  
         Case 2  
           SendMessage_(GadgetID(4), #EM_REDO, #Null, #Null)  
         Case 3, 4  
           SendMessage_(GadgetID(4), #EM_GETSEL, @Min,@Max)  
   
          If Min<>Max  
            If Min<Max  
              xx=Max-Min  
            Else  
              xx=Min-Max  
            EndIf  
   
           lpText.s=Space(xx)  
           SendMessage_(GadgetID(4), #EM_GETSELTEXT, #Null, @lpText)  
           SetClipboardText(lpText)  
   
           If Menu=3  
             SendMessage_(GadgetID(4),#EM_REPLACESEL,0,@"")  
           EndIf  
   
          EndIf  
   
         Case 5  
           Text.s=GetClipboardText()  
           SendMessage_(GadgetID(4),#EM_REPLACESEL,0,@Text)  
         Case 6  
           SendMessage_(GadgetID(4),#EM_SETSEL,0,-1)  
         EndSelect  
       EndIf

     Until Event=#PB_Event_CloseWindow 
     
EndIf          
End