Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

Serial communication

ericci
2018-03-23
2021-12-04
  • ericci - 2018-03-23

    Hi all!

    I need to establish a serial communication (UART) with my own board that talks a proprietary protocol and i must configure PC side codesys application as a slave i.e. always listening for a command and send back an acknowledge message. The communication hasn't any flow control.
    After readings and studying '' '' i've got stuck into the reception of unknown number of bytes from the peripheral due to the asynchronous way to transmit data with UART.
    As my PRG it execute reading and writing routines at a fixed rate and i don't know how to set a callback to a byte reception interrupt (for example) or manage any kind of circular buffer (avoiding loss of data and) i'm asking to the community if someone faced the problem in the past and if there exist some example to be shared.
    I'm even thinking of using python scripting but i preferred to ask you before.

    I think that a solution could be found out because of Modbus RTU protocol support.

    Can you help me?

    Regards

     
  • Anonymous - 2018-03-24

    Originally created by: ph0010421

    Hello
    There may be a better answer, but I execute the COM.Read FB like this:

       fbiComRead(
          xExecute := NOT fbiComRead.xDone,
          hCom := Handle,
          pBuffer := ADR(JunkString),
          szBuffer := 255
          );
          
    

    And concatenate the received string using ```

    fbiComRead.szSize

    ```

    I've written my own FB to build the final string (and look for a termination character) if you want it?

    I've used this method with devices that send serial data (115K2) and it's reliable.
    Pp

     
  • ericci - 2018-03-24

    Hello pp,
    I don't believe your snippet is 100% reliable for the reason that your fb is executed at a known fixed rate and for one cycle it isn't in executing state at least. I'm could be wrong if function and fb behind COM.read continuously listen for a packet and isn't driven by argument parameter 'execute'.
    In facts i would like to ask to everyone where i can find detailed documentation of 'Model behaviour library' and 'job executing library' that Com library it depends?

    Thank you,
    Enrico

     
  • ericci - 2018-03-24

    I'm interested in understanding if there exist some chance to write code for an event driven task. In this case the event would be byte received from com port.

     
  • Anonymous - 2018-03-24

    Originally created by: ph0010421

    Hello Enrico
    I'm interested to discuss things like this.
    My understanding is the serial buffer is filled asynchronously, so the rate that the Read fb is executed doesn't matter.
    As long as the szBuffer is larger than the maximum number of characters that can be received between executing then it's reliable.
    The fb's reliability is based on an extended stress test.

    The szSize is sometimes more than 1; ie multiple characters were received in 1 scan.

    Best wishes

     
  • Anonymous - 2018-03-25

    Originally created by: Viacheslav Mezentsev

    ericci hat geschrieben:
    and i don't know how to set a callback to a byte reception interrupt (for example)

    It is impossible for the codesys application (IRQ handling). This is normal for microcontrollers, but not for the user application in the Codesys RTS.

    But I can suggest you the following algorithm.

    And code example:

    // Сервер Modbus RTU (slave)
    function_block TModbusRTUSlave implements ISerialSettings
    var_input
       xEnable: bool;
    end_var
    var_output
       xError: bool;   // ошибка
        resIEC: RTS_IEC_RESULT;
    end_var
    var constant
        MODE_IDLE:  byte := 0;
        MODE_OPEN:  byte := 1;
        MODE_SETUP: byte := 2;
        MODE_CLOSE: byte := 3;
        MODE_ERROR: byte := 4;
        MODE_EVENT: byte := 5;      
        MODE_WORK:  byte := 6;
        MODE_CLEAR: byte := 7;   
    end_var
    var
       _deviceId: byte;
       _settings: COM_Settings;
       _outData: TByteArray;
       _inData: TByteArray;   
    end_var
    var
        bFound: bool;
        n, k, nfdReady, saSize: dint; 
        nRecv: udint;
        mode: uint := MODE_IDLE;
        rt: r_trig; 
        ft: f_trig;
        result: RTS_IEC_RESULT := Errors.ERR_OK;    
        hListener: RTS_IEC_HANDLE;
        pHeader: pointer to MBREQUESTHEADER;
        tmpbuf: array [ 0 .. 255 ] of byte;
    end_var
    //---------------------------------------------------
    if not xEnable then 
       
       xError := false;
        resIEC := Errors.ERR_OK;
       mode := MODE_IDLE;
     
    end_if
    rt( clk := xEnable );
    if rt.q then
       mode := MODE_OPEN;
    end_if
    ft( clk := xEnable );
    if ft.q then
       mode := MODE_CLOSE;
    end_if
    case mode of
          
       MODE_OPEN:
       
          // Открываем устройство.
          hListener := SysComOpen( _settings.sPort, adr( resIEC ) );
          
            // Формирование флага ошибки.
            xError := ( resIEC <> Errors.ERR_OK );
            
            mode := sel( xError, MODE_SETUP, MODE_ERROR );
            
          
        MODE_SETUP:
            // Настраиваем устройство.
            resIEC := SysComSetSettings( hCom := hListener, pSettings := adr( _settings ), _ := 0 );
        
            // Формирование флага ошибки.
            xError := ( resIEC <> Errors.ERR_OK );
            
            mode := sel( xError, MODE_CLEAR, MODE_ERROR );
       
            
        // Очистка приёмного буфера.
       MODE_CLEAR:
          _inData.Clear();
       
          result := SysComPurge( hListener );
          
          mode := MODE_EVENT;
            
            
        // Ожидание событий.
        MODE_EVENT:
            
          nRecv := Receive( hListener, buf => tmpbuf, result => result );
          
          if nRecv > 0 then
          
             _inData.AddRange( adr( tmpbuf ), udint_to_uint( nRecv ) );
          
             mode := MODE_WORK;
             
          end_if
            
            
       // Обработка запросов.
       MODE_WORK:
       
            // Считываем данные.      
            nRecv := Receive( hListener, buf => tmpbuf, result => result );
            if result = Errors.ERR_OK then
                
                // Если есть данные.
                if nRecv > 0 then                    
        
                    _inData.AddRange( adr( tmpbuf ), udint_to_uint( nRecv ) );
                
                else
                    if _inData.Size > 0 then
                        
                        // Проверяем идентификатор протокола и номер устройства.
                        pHeader := _inData.Bytes;
                        
                        if pHeader^.StationAddress = _deviceId then
            
                            // Обрабатываем запрос.
                            OnReceive( hListener, _inData );
                                
                        end_if                                         
                        
                    else
                        
                        OnFailure( Errors.ERR_SIZE_MISMATCH );
                        
                    end_if
                    
                    mode := MODE_CLEAR;
                    
                end_if
                
            else
                
                OnFailure( result );
                
                mode := MODE_CLEAR;
                
            end_if                        
                                   
       
       // Закрываем устройство.
       MODE_CLOSE:
          
          if hListener <> RTS_INVALID_HANDLE then
          
             result := SysComClose( hCom := hListener );
             
             hListener := RTS_INVALID_HANDLE;
       
          end_if
            mode := MODE_IDLE;
            
       // Ошибка.
       MODE_ERROR: ;
     
        MODE_IDLE: ;
        
    end_case
    

    IMG: 2018

     
  • ericci - 2018-03-25

    Hello Viacheslav,

    Thank you for your support and i hope this will clarify the matter in the future.
    I suppose your code is good enough to implement such a modbus slave protocol provided that your receiving routine is much faster than the sending one (Master side), and you accept that a slave could loss some packet (rarely).
    Last assertion is due to the fact that a slave could start to read a packet when master is transmitting one and if slave receives his address and a valid modbus command into the master's packet payload only final crc it avoid a wrong command reception.
    Hope to be clear.

    Those examples are common problem related to an asynchronous packet exchange although tomorrow i will try your snippet in my code.

    Regards

     
  • Anonymous - 2018-03-25

    Originally created by: Viacheslav Mezentsev

    Yes, this code must be improved by adding processing of some special cases. You are right, the function block must be executed in a sufficiently fast separate task.

    You can try how it works. This code is part of the AgavaModbus library (see AgavaSDK package here - all docs in russian). See example: Примеры\AgavaModbus\ПЛК-40\ST\ModbusRTUSlave.project . You must install AgavaLibraries.3.5.10.3.package first (for Codesys >= 3.5.10.x).

     
    • damian177 - 2021-12-04

      Hi, Where I can download AgavaLibraries ?

       

Log in to post a comment.