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;