Wago CC100 - Internal RS485 port - unable to get communication at 300bd(speed)

Hi,

I’m trying to read data from an energy meter using IEC 62056-21 protocol. The protocol requires initial communication at 300 baud, but I’m unable to get this working on a Wago CC100 using the internal serial port.

I’ve tested various speeds with the code below, and it works fine at 1200 baud and higher. However, when I change the speed to 300 baud, there’s no traffic on the bus - complete silence.

Has anyone found a way to achieve reliable communication at such low baud rates on this hardware?

Hardware details:

  • Wago CC100 751-9401 (with CAN interface)
  • Latest firmware installed
  • Code compiled and tested with CODESYS V3.5 SP19 Patch 7

Issue: Communication works at ≥1200 baud but fails completely at 300 baud (required by IEC 62056-21 standard).

Any suggestions or workarounds would be greatly appreciated.

(*
===============================================================================
Continuous COM Test - do podglądania komunikacji w terminalu
Wysyła pakiety w nieskończonej pętli z konfigurowalną prędkością
===============================================================================
*)

PROGRAM ContinuousComTest
VAR_INPUT
    xStartTest          : BOOL := FALSE;        // Uruchom test
    xStopTest           : BOOL := FALSE;        // Zatrzymaj test
    udiBaudRate         : UDINT := 300;        // Prędkość transmisji (edytowalny)
    tSendInterval       : TIME := T#4S;         // Czas między pakietami (edytowalny)
    iPacketType         : INT := 1;             // Typ pakietu do wysyłania (1-4)
END_VAR

VAR_OUTPUT
    // Status testu - OBSERWUJ W CODESYS
    xTestRunning        : BOOL := FALSE;        // Test w trakcie
    xComReady           : BOOL := FALSE;        // COM gotowy do pracy
    sCurrentStatus      : STRING(100) := '';   // Aktualny status
    sLastError          : STRING(255) := '';   // Ostatni błąd
    
    // Statystyki wysyłania - OBSERWUJ W CODESYS
    udiPacketsSent      : UDINT := 0;           // Liczba wysłanych pakietów
    udiBytesSent        : UDINT := 0;           // Liczba wysłanych bajtów
    udiBytesReceived    : UDINT := 0;           // Liczba odebranych bajtów (jeśli coś wróci)
    sLastSentPacket     : STRING(50) := '';    // Ostatni wysłany pakiet
    sLastReceivedData   : STRING(100) := '';   // Ostatnie odebrane dane
    dtLastSent          : DATE_AND_TIME;        // Czas ostatniego wysłania
    
    // DEBUG - sprawdzenie bajtów - OBSERWUJ W CODESYS
    byteDebug0          : BYTE;                 // arrTxBuffer[0]
    byteDebug1          : BYTE;                 // arrTxBuffer[1]
    byteDebug2          : BYTE;                 // arrTxBuffer[2]
    byteDebug3          : BYTE;                 // arrTxBuffer[3]
    byteDebug4          : BYTE;                 // arrTxBuffer[4]
    udiDebugTxCount     : UDINT;                // Ile bajtów do wysłania
    
    // DEBUG CYKLU - OBSERWUJ TO!

END_VAR

VAR
    // COM interface
    ComPort1            : FbSerialInterface_internal(1);
    CLIENT_ID           : WagoClientIdentification := 16#0001;
    
    // State machine
    eState              : INT := 0;             // 0=idle, 1=init, 2=running, 99=error
    eStateOld           : INT := 0;
    
    // Timers
    fbSendTimer         : TON;                  // Timer między pakietami
    fbReceiveCheck      : TON;                  // Timer sprawdzania odbioru
    
    // Buffers
    arrTxBuffer         : ARRAY[0..99] OF BYTE;
    arrRxBuffer         : ARRAY[0..99] OF BYTE;
    udiTxCount          : UDINT;
    udiRxCount          : UDINT;
    udiTxBytesWritten   : UDINT;
    udiRxBytesRead      : UDINT;
    
    // Working variables
    eResult             : eResultCode;
    i                   : UDINT;
    xStartOld           : BOOL := FALSE;
    xStopOld            : BOOL := FALSE;
    
    // Send state control - DODANE DO ROZWIĄZANIA PROBLEMU CYKLU
    xSendInProgress     : BOOL := FALSE;    // Czy wysyłanie w trakcie
    xSendRequested      : BOOL := FALSE;    // Czy zażądano wysłania
    
    // Test packets - różne typy do wysyłania
    sPacket1            : STRING := 'HELLO\r\n';
    sPacket2            : STRING := 'TEST123\r\n';
    sPacket3            : STRING := '/?!\r\n';          // IEC62056-like
    sPacket4            : STRING := 'WAGO_TEST_PACKET\r\n';
    sCurrentPacket      : STRING(50);
    
    // Packet counter for cycling
    iCurrentPacketNum   : INT := 1;
END_VAR
// Edge detection
IF xStartTest AND NOT xStartOld THEN
    IF eState = 0 OR eState = 99 THEN
        eState := 1; // Go to init
    END_IF;
END_IF;
xStartOld := xStartTest;

IF xStopTest AND NOT xStopOld THEN
    eState := 0; // Stop everything
END_IF;
xStopOld := xStopTest;

// State change detection
IF eState <> eStateOld THEN
    eStateOld := eState;
    fbSendTimer(IN := FALSE); // Reset timer on state change
    fbReceiveCheck(IN := FALSE);
END_IF;

// Main state machine
CASE eState OF
    
    0: // IDLE
        xTestRunning := FALSE;
        xComReady := FALSE;
        sCurrentStatus := 'IDLE - Set xStartTest=TRUE to begin';
        
        // Reset counters when idle
        IF NOT xStartTest THEN
            udiPacketsSent := 0;
            udiBytesSent := 0;
            udiBytesReceived := 0;
            sLastSentPacket := '';
            sLastReceivedData := '';
            sLastError := '';
        END_IF;
    
    1: // INITIALIZE COM
        sCurrentStatus := 'Initializing COM port...';
        xTestRunning := TRUE;
        
        // Step 1: Attach exclusively
        eResult := ComPort1.AttachExclusively(CLIENT_ID);
        IF eResult <> 0 THEN
            IF eResult = EBUSY THEN
                sLastError := 'COM port busy - another client using it';
            ELSE
                sLastError := CONCAT('Attach error: ', DINT_TO_STRING(eResult));
            END_IF;
            eState := 99; // Error
            RETURN;
        END_IF;
        
        // Step 2: Configure with user-specified baud rate
        eResult := ComPort1.Configure(
            RequestorID := CLIENT_ID,
            udiBaudrate := udiBaudRate,
            usiDataBits := 8,
            eParity := WagoTypesCom.eTTYParity.None,
            eStopbits := WagoTypesCom.eTTYStopBits.One,
            eHandshake := WagoTypesCom.eTTYHandshake.None,
            ePhysical := WagoTypesCom.eTTYPhysicalLayer.RS485_HalfDuplex,
            uiSystemBufferSize := 1024
        );
        
        IF eResult <> 0 THEN
            // Try RS485 if RS232 failed
            eResult := ComPort1.Configure(
                RequestorID := CLIENT_ID,
                udiBaudrate := udiBaudRate,
                usiDataBits := 8,
                eParity := WagoTypesCom.eTTYParity.None,
                eStopbits := WagoTypesCom.eTTYStopBits.One,
                eHandshake := WagoTypesCom.eTTYHandshake.None,
                ePhysical := WagoTypesCom.eTTYPhysicalLayer.RS485_HalfDuplex,
                uiSystemBufferSize := 1024
            );
            
            IF eResult <> 0 THEN
                sLastError := 'Configure failed for both RS232 and RS485';
                eState := 99; // Error
                RETURN;
            END_IF;
        END_IF;
        
        // Step 3: Open port
        eResult := ComPort1.Open(CLIENT_ID);
        IF eResult <> 0 THEN
            sLastError := CONCAT('Open failed: ', DINT_TO_STRING(eResult));
            eState := 99; // Error
            RETURN;
        END_IF;
        
        // Success - ready to send
        xComReady := TRUE;
        sCurrentStatus := CONCAT('COM ready at ', UDINT_TO_STRING(udiBaudRate));
        sCurrentStatus := CONCAT(sCurrentStatus, ' baud - starting transmission loop');
        eState := 2; // Go to running state
        
        // Clear buffers and start fresh
        ComPort1.ClearBuffers(CLIENT_ID);
        iCurrentPacketNum := 1;
    
    2: // RUNNING - Continuous packet sending
        sCurrentStatus := CONCAT('RUNNING - Sending every ', TIME_TO_STRING(tSendInterval));
        
        // Timer for packet sending - trigger send request
        fbSendTimer(IN := TRUE, PT := tSendInterval);
        
        // Request send when timer expires and no send in progress
        IF fbSendTimer.Q AND NOT xSendInProgress THEN
            xSendRequested := TRUE;
            fbSendTimer(IN := FALSE); // Reset timer
        END_IF;
        
        // Execute send only when requested and not already in progress
        IF xSendRequested AND NOT xSendInProgress THEN
            xSendRequested := FALSE;
            xSendInProgress := TRUE;
            
            // Clear any previous data in buffer
            FOR i := 0 TO 99 DO
                arrTxBuffer[i] := 0;
            END_FOR;
            
            // Select packet to send based on iPacketType
            CASE iPacketType OF
                1: // HELLO packet - direct byte assignment
                    arrTxBuffer[0] := 16#48;  // 'H'
                    arrTxBuffer[1] := 16#45;  // 'E'
                    arrTxBuffer[2] := 16#4C;  // 'L'
                    arrTxBuffer[3] := 16#4C;  // 'L'
                    arrTxBuffer[4] := 16#4F;  // 'O'
                    arrTxBuffer[5] := 16#0D;  // CR
                    arrTxBuffer[6] := 16#0A;  // LF
                    udiTxCount := 7;
                    sCurrentPacket := 'HELLO\\r\\n';
                    
                2: // TEST123 packet
                    arrTxBuffer[0] := 16#54;  // 'T'
                    arrTxBuffer[1] := 16#45;  // 'E'
                    arrTxBuffer[2] := 16#53;  // 'S'
                    arrTxBuffer[3] := 16#54;  // 'T'
                    arrTxBuffer[4] := 16#31;  // '1'
                    arrTxBuffer[5] := 16#32;  // '2'
                    arrTxBuffer[6] := 16#33;  // '3'
                    arrTxBuffer[7] := 16#0D;  // CR
                    arrTxBuffer[8] := 16#0A;  // LF
                    udiTxCount := 9;
                    sCurrentPacket := 'TEST123\\r\\n';
                    
                3: // IEC62056-like packet /?!
                    arrTxBuffer[0] := 16#2F;  // '/'
                    arrTxBuffer[1] := 16#3F;  // '?'
                    arrTxBuffer[2] := 16#21;  // '!'
                    arrTxBuffer[3] := 16#0D;  // CR
                    arrTxBuffer[4] := 16#0A;  // LF
                    udiTxCount := 5;
                    sCurrentPacket := '/?!\\r\\n';
                    
                4: // WAGO_TEST packet
                    arrTxBuffer[0] := 16#57;  // 'W'
                    arrTxBuffer[1] := 16#41;  // 'A'
                    arrTxBuffer[2] := 16#47;  // 'G'
                    arrTxBuffer[3] := 16#4F;  // 'O'
                    arrTxBuffer[4] := 16#5F;  // '_'
                    arrTxBuffer[5] := 16#54;  // 'T'
                    arrTxBuffer[6] := 16#45;  // 'E'
                    arrTxBuffer[7] := 16#53;  // 'S'
                    arrTxBuffer[8] := 16#54;  // 'T'
                    arrTxBuffer[9] := 16#0D;  // CR
                    arrTxBuffer[10] := 16#0A; // LF
                    udiTxCount := 11;
                    sCurrentPacket := 'WAGO_TEST\\r\\n';
                    
                ELSE
                    // Cycle through all packets - use direct bytes too
                    CASE iCurrentPacketNum OF
                        1: // HELLO
                            arrTxBuffer[0] := 16#48; arrTxBuffer[1] := 16#45; arrTxBuffer[2] := 16#4C; 
                            arrTxBuffer[3] := 16#4C; arrTxBuffer[4] := 16#4F; arrTxBuffer[5] := 16#0D; arrTxBuffer[6] := 16#0A;
                            udiTxCount := 7; sCurrentPacket := 'HELLO\\r\\n';
                        2: // TEST123
                            arrTxBuffer[0] := 16#54; arrTxBuffer[1] := 16#45; arrTxBuffer[2] := 16#53; arrTxBuffer[3] := 16#54;
                            arrTxBuffer[4] := 16#31; arrTxBuffer[5] := 16#32; arrTxBuffer[6] := 16#33; 
                            arrTxBuffer[7] := 16#0D; arrTxBuffer[8] := 16#0A;
                            udiTxCount := 9; sCurrentPacket := 'TEST123\\r\\n';
                        3: // /?!
                            arrTxBuffer[0] := 16#2F; arrTxBuffer[1] := 16#3F; arrTxBuffer[2] := 16#21; 
                            arrTxBuffer[3] := 16#0D; arrTxBuffer[4] := 16#0A;
                            udiTxCount := 5; sCurrentPacket := '/?!\\r\\n';
                        4: // WAGO_TEST
                            arrTxBuffer[0] := 16#57; arrTxBuffer[1] := 16#41; arrTxBuffer[2] := 16#47; arrTxBuffer[3] := 16#4F;
                            arrTxBuffer[4] := 16#5F; arrTxBuffer[5] := 16#54; arrTxBuffer[6] := 16#45; arrTxBuffer[7] := 16#53;
                            arrTxBuffer[8] := 16#54; arrTxBuffer[9] := 16#0D; arrTxBuffer[10] := 16#0A;
                            udiTxCount := 11; sCurrentPacket := 'WAGO_TEST\\r\\n';
                        ELSE 
                            iCurrentPacketNum := 1;
                            arrTxBuffer[0] := 16#48; arrTxBuffer[1] := 16#45; arrTxBuffer[2] := 16#4C; 
                            arrTxBuffer[3] := 16#4C; arrTxBuffer[4] := 16#4F; arrTxBuffer[5] := 16#0D; arrTxBuffer[6] := 16#0A;
                            udiTxCount := 7; sCurrentPacket := 'HELLO\\r\\n';
                    END_CASE;
                    
                    iCurrentPacketNum := iCurrentPacketNum + 1;
                    IF iCurrentPacketNum > 4 THEN
                        iCurrentPacketNum := 1;
                    END_IF;
            END_CASE;
            
            // Send packet - TYLKO RAZ!
            eResult := ComPort1.Write(
                RequestorID := CLIENT_ID,
                pTxBuffer := ADR(arrTxBuffer),
                udiTxNBytes := udiTxCount,
                udiTxNBytesTransferred => udiTxBytesWritten
            );
            
            // DEBUG - copy first few bytes for observation
            byteDebug0 := arrTxBuffer[0];
            byteDebug1 := arrTxBuffer[1];
            byteDebug2 := arrTxBuffer[2];
            byteDebug3 := arrTxBuffer[3];
            byteDebug4 := arrTxBuffer[4];
            udiDebugTxCount := udiTxCount;
            
            IF eResult = 0 THEN
                // Success
                udiPacketsSent := udiPacketsSent + 1;
                udiBytesSent := udiBytesSent + udiTxBytesWritten;
                sLastSentPacket := sCurrentPacket;
              
                
                // Update status with packet info
                sCurrentStatus := CONCAT('Sent packet #', UDINT_TO_STRING(udiPacketsSent));
                sCurrentStatus := CONCAT(sCurrentStatus, ': ');
                sCurrentStatus := CONCAT(sCurrentStatus, LEFT(sCurrentPacket, 20)); // First 20 chars
                
                // Send completed successfully
                xSendInProgress := FALSE;
                
            ELSE
                sLastError := CONCAT('Send failed: ', DINT_TO_STRING(eResult));
                xSendInProgress := FALSE;  // Reset even on error
                eState := 99; // Error
            END_IF;
        END_IF;
        
        // Check for any received data (every 100ms)
        fbReceiveCheck(IN := TRUE, PT := T#100MS);
        IF fbReceiveCheck.Q THEN
            fbReceiveCheck(IN := FALSE);
            
            eResult := ComPort1.Read(
                RequestorID := CLIENT_ID,
                pRxBuffer := ADR(arrRxBuffer),
                udiRxBufferSize := SIZEOF(arrRxBuffer),
                udiRxNBytes => udiRxBytesRead
            );
            
            IF eResult = 0 AND udiRxBytesRead > 0 THEN
                udiBytesReceived := udiBytesReceived + udiRxBytesRead;
                
                // Convert to readable string
                sLastReceivedData := '';
                FOR i := 0 TO MIN(udiRxBytesRead - 1, 50) DO
                    IF arrRxBuffer[i] >= 32 AND arrRxBuffer[i] <= 126 THEN
                        sLastReceivedData := CONCAT(sLastReceivedData, BYTE_TO_STRING(arrRxBuffer[i]));
                    ELSE
                        sLastReceivedData := CONCAT(sLastReceivedData, '.');
                    END_IF;
                END_FOR;
            END_IF;
        END_IF;
        
        // Check if stop requested
        IF xStopTest THEN
            eState := 0; // Stop gracefully
        END_IF;
    
    99: // ERROR
        sCurrentStatus := CONCAT('ERROR: ', sLastError);
        xTestRunning := TRUE; // Still running but in error
        xComReady := FALSE;
        
        // Try to cleanup
        ComPort1.Close(CLIENT_ID);
        ComPort1.ReleaseExclusivity(CLIENT_ID);
        
        // Auto-reset to idle after some time or manual stop
        IF xStopTest THEN
            eState := 0;
        END_IF;
        
END_CASE;

// Cleanup when stopping
IF eState = 0 AND eStateOld <> 0 THEN
    // Clean shutdown
    ComPort1.Close(CLIENT_ID);
    ComPort1.ReleaseExclusivity(CLIENT_ID);
    xTestRunning := FALSE;
    xComReady := FALSE;
END_IF;

I’m afraid the manual states that the CAN interface supports a minimal baud rate of 10 kbit/s :confused:

But i have problem with serial interface - not CAN - that works great;)