Introdução

O TISOTCPDriver fala o protocolo S7comm sobre ISO-on-TCP (RFC 1006, porta TCP 102) — o mesmo que o STEP 7, o TIA Portal e a LibNodave usam para ler e escrever memórias de um CLP Siemens. O driver é uma reescrita em Pascal do ISOTCP da LibNodave de Thomas Hergenhahn: não depende de nenhuma DLL ou biblioteca externa.

Ele conversa com:

  • S7-300, S7-400, ET200S/ET200SP com CPU ou CP Ethernet;
  • S7-1200 e S7-1500 — depois de liberar o acesso PUT/GET e desligar o acesso otimizado dos DBs (veja Preparando o CLP);
  • S7-200 através de um CP 243-1 (ConnectionWay = ISOTCP_VIA_CP243);
  • LOGO! 0BA7/0BA8 (LOGO! 7 e 8), que expõe a área V como DB1;
  • qualquer simulador ou gateway compatível com S7comm (PLCSIM com NetToPLCSim, Snap7 server, etc.).

Ele não conversa com o S7-1200/1500 pelo protocolo nativo deles (S7CommPlus, que é o que o TIA Portal usa para acesso simbólico) — para isso existe o TS7CommPlusDriver. O ISOTCP continua sendo a opção mais simples para esses CLPs quando você controla o projeto no TIA e pode desligar o acesso otimizado.

Componentes necessários
  1. Uma TTCP_UDPPort com Host = IP do CLP, Port = 102 (padrão) e PortType = ptTCP. Veja Portas de comunicação.
  2. Um TISOTCPDriver com CommunicationPort apontando para a porta.
  3. Os tags (TPLCTagNumber, TPLCBlock, TPLCStruct, TPLCString) com ProtocolDriver apontando para o driver.

O jeito mais rápido de chegar ao passo 3 é clicar com o botão direito no TISOTCPDriver e escolher Tag Builder (veja abaixo).

Propriedades do driver
Propriedade Padrão Descrição
CommunicationPort – A porta TTCP_UDPPort usada pelo driver.
PLCRack 0 Rack da CPU.
PLCSlot 0 Slot da CPU.
PLCStation 2 Endereço da estação. Só tem significado em redes MPI/PROFIBUS atrás de um gateway; em Ethernet deixe o padrão.
ConnectionWay ISOTCP ISOTCP para conexão direta à CPU/CP Ethernet; ISOTCP_VIA_CP243 para S7-200 atrás de um CP 243-1.
ISOTCPConnType ctOP Tipo de conexão que o driver se identifica ao CLP: ctPG (programadora), ctOP (painel de operação) ou ctBasic (outra). O CLP reserva recursos por tipo; se todas as conexões OP estiverem em uso, troque para ctBasic.
ForcePDUSize pduAuto Tamanho de PDU negociado. pduAuto pede o maior que o CLP aceita; force pdu240 para CLPs antigos ou LOGO!, que não negociam corretamente.
ReadOnly False Se True, o driver recusa qualquer escrita (ioReadOnlyProtocol). Útil para uma tela de supervisão que nunca deve alterar o processo.
ReadSomethingAlways True Mantém a conexão viva lendo algum tag a cada ciclo mesmo que nenhum precise ser atualizado.

Uma conexão ISOTCP fala com um único CLP. Por isso, as propriedades PLCRack, PLCSlot e PLCStation dos tags são ignoradas: o que vale é o que está configurado no driver. Para falar com dois CLPs, use duas portas e dois drivers.

Valores de rack/slot que funcionam na maioria dos casos:

CPU PLCRack PLCSlot
S7-300 (CPU com PN integrada ou CP 343-1) 0 2
S7-400 0 slot físico da CPU (normalmente 2 ou 3)
S7-1200 / S7-1500 0 0 (1 também funciona no 1200)
ET200S / ET200SP CPU 0 2 (ET200S) / 0 (ET200SP)
S7-200 via CP 243-1 0 0
LOGO! 7 / 8 0 0 (ver Preparando o CLP)

Em código, UpdatePLCAddress(Rack, Slot, Station) troca os três de uma vez sem reiniciar a conexão três vezes.

Preparando o CLP

S7-300 / S7-400: nada a fazer além de ter o IP configurado. Se a CPU tiver senha de proteção contra escrita, o driver lê mas não escreve.

S7-1200 / S7-1500 (no TIA Portal):

  1. Nas propriedades da CPU → Protection & Security → Connection mechanisms: marque Permit access with PUT/GET communication from remote partner.
  2. Em cada DB que o PascalSCADA vai acessar, desmarque Optimized block access (propriedades do DB → Attributes) e recompile. Um DB otimizado não tem endereços absolutos, e o ISOTCP só endereça por byte.
  3. Se o nível de proteção da CPU for Full access, nada mais. Com Read access o driver só lê; com HMI access ou No access a conexão é recusada.

LOGO! 7 / 8 (no LOGO!Soft Comfort): em Tools → Ethernet connections, adicione uma conexão de servidor com TSAP local 20.00 e TSAP remoto 10.00, ou marque Allow all connections. A área V (VB/VW/VD) aparece para o PascalSCADA como DB1 (MemReadFunction = 4, MemFile_DB = 1); as memórias M como área M (MemReadFunction = 3). O exemplo examples/demo_logo8 mostra isso, com ForcePDUSize = pdu240 e PLCStation = 0.

S7-200: use ConnectionWay = ISOTCP_VIA_CP243 e configure uma conexão no CP 243-1 pelo assistente do STEP 7-Micro/WIN. A área V é MemReadFunction = 4 com MemFile_DB = 1.

Endereçando memórias

Todo endereço S7 é composto de uma área, um número de DB (só para a área DB), um endereço em bytes e o tipo da variável. No tag isso vira:

Propriedade do tag Preencha com
MemReadFunction Código da área (tabela abaixo).
MemFile_DB Número do DB, quando a área for 4. Ignorado nas outras.
MemAddress Endereço do byte inicial (o número depois de IB, MW, DBD…).
TagType Tipo da variável (tabela mais abaixo).
MemWriteFunction, MemSubElement, PLCRack/Slot/Station Não usados. Deixe em zero.

Códigos de área para MemReadFunction:

Código Área CPUs Observação
1 I — entradas digitais (imagem de processo) todas
2 Q — saídas digitais (imagem de processo) todas
3 M — memórias/flags todas
4 DB — bloco de dados 300/400/1200/1500; V no S7-200 e LOGO! Preencha MemFile_DB.
5 C — contadores 300/400 TagType = pttWord.
6 T — temporizadores 300/400 TagType = pttWord.
7 SM — memória especial S7-200
8 AI — entradas analógicas S7-200 TagType = pttWord.
9 AQ — saídas analógicas S7-200 TagType = pttWord.
10 C — contadores S7-200 TagType = pttWord.
11 T — temporizadores S7-200 TagType = pttWord.
12 PIW — periferia de entrada 300/400 TagType = pttWord. Lê o valor direto do cartão, sem passar pela imagem de processo.

Tipos de dados S7 e o TagType correspondente:

Tipo no CLP Bytes TagType SwapBytes SwapWords SwapDWords
BOOL – pttByte no tag + um TTagBit por bit
BYTE, CHAR, USINT 1 pttByte
SINT 1 pttShortInt
WORD, UINT, S5TIME, DATE 2 pttWord ✔
INT 2 pttSmallInt ✔
DWORD, UDINT, TIME_OF_DAY 4 pttDWord ✔ ✔
DINT, TIME 4 pttLongInt ✔ ✔
REAL 4 pttFloat ✔ ✔
LINT, LTIME 8 pttInt64 ✔ ✔ ✔
ULINT, LWORD 8 pttQWord ✔ ✔ ✔
LREAL 8 pttDouble ✔ ✔ ✔
STRING[n] n + 2 TPLCString com StringType = stSIEMENS

Os CLPs Siemens guardam os valores em big-endian e o PascalSCADA monta os bytes na ordem da máquina (little-endian). Por isso tudo que tem mais de um byte precisa dos Swap* marcados como na tabela — sem eles, um INT = 1 aparece como 256 e um REAL vira um número absurdo. O Tag Builder já marca os swaps corretos.

Exemplos:

Endereço S7 MemReadFunction MemFile_DB MemAddress TagType Swaps
IB3 1 0 3 pttByte –
Q0.5 2 0 0 pttByte + TTagBit com StartBit = EndBit = 5 –
MW10 3 0 10 pttWord B
MD100 (como REAL) 3 0 100 pttFloat B + W
DB5.DBW20 4 5 20 pttSmallInt B
DB5.DBD24 (como DINT) 4 5 24 pttLongInt B + W
DB10.DBB0 até DBB49 4 10 0 TPLCBlock pttByte, Size = 50 –
DB12 STRING[30] em DBB8 4 12 8 TPLCString, StringSize = 30, stSIEMENS –
VW6 (LOGO!/S7-200) 4 1 6 pttSmallInt B
T5 (S7-300) 6 0 5 pttWord B
Bits

O ISOTCP do PascalSCADA lê e escreve por byte. Para um BOOL (uma entrada, uma saída, um bit de um DB), crie um TPLCTagNumber com TagType = pttByte apontando para o byte que contém o bit e um TTagBit para cada bit de interesse (StartBit = EndBit = número do bit). A escrita no TTagBit lê o byte atual, altera só aquele bit e grava o byte de volta.

Para muitos bits no mesmo byte, clique com o botão direito no tag e escolha Map bits: ele cria os oito TTagBit de uma vez. O exemplo examples/laz_isotcp_mapping_bits_from_other_tag faz isso com DB1.DBB0.

Blocos, estruturas e strings
  • Um TPLCBlock lê Size variáveis consecutivas do mesmo tipo em uma requisição — um array de REAL num DB, por exemplo (TagType = pttFloat, Size = número de elementos, MemAddress = byte inicial). Os swaps valem para o bloco inteiro.
  • Um TPLCStruct lê Size bytes consecutivos, e cada TPLCStructItem/TPLCStructString interpreta um trecho com seu próprio tipo e swap. É a forma natural de mapear um DB com campos mistos ou uma UDT — veja o exemplo em Tags.
  • Um TPLCString com StringType = stSIEMENS lê uma STRING[n]: os dois bytes de cabeçalho (tamanho máximo e tamanho atual) são tratados pelo driver. Use stC só para arrays de CHAR terminados em zero.

O driver junta automaticamente tags vizinhos da mesma área e do mesmo RefreshTime em uma requisição só, respeitando o tamanho da PDU negociado. Com pduAuto numa CPU moderna cabem até 1920 bytes de dados por mensagem; num S7-300 antigo ou LOGO!, 240. Se o AvgUpdateRate dos tags ficar muito acima do RefreshTime, o CLP não está dando conta — aumente o RefreshTime dos tags menos críticos ou agrupe-os em blocos.

Tag Builder

Botão direito no TISOTCPDriver → Tag Builder abre o assistente Siemens S7 Tag builder:

  1. Escolha a área (entradas, saídas, flags, DB, contadores, timers, áreas do S7-200…), o tipo de tag a criar (TPLCTagNumber separados, um TPLCBlock, ou TPLCStruct) e o tempo de varredura.
  2. Para tags separados ou bloco: informe o tipo de dado, o endereço inicial, a quantidade e o DB. O assistente já marca SwapBytes/SwapWords conforme o tipo.
  3. Para estruturas: monte a lista de itens (nome, tipo, swap, scan) na aba Items declaration; o assistente calcula os offsets e pode gerar N estruturas iguais em sequência (útil para arrays de UDT).

Os componentes criados aparecem no form/datamodule com ProtocolDriver já ligado.

Exemplo passo a passo

Ler a temperatura DB1.DBD0 (REAL) e escrever o setpoint DB1.DBW4 (INT) de um S7-1200 em 192.168.0.10:

  1. No TIA Portal: libere PUT/GET, desmarque Optimized block access no DB1, compile e carregue.
  2. Solte um TTCP_UDPPort: Host = 192.168.0.10, Port = 102, Active = True.
  3. Solte um TISOTCPDriver: CommunicationPort = TCP_UDPPort1, PLCRack = 0, PLCSlot = 0.
  4. Solte um TPLCTagNumber chamado Temperatura: ProtocolDriver = ISOTCPDriver1, MemReadFunction = 4, MemFile_DB = 1, MemAddress = 0, TagType = pttFloat, SwapBytes = True, SwapWords = True.
  5. Solte outro chamado Setpoint: idem, com MemAddress = 4, TagType = pttSmallInt, SwapBytes = True.
  6. Ligue um THMILabel em Temperatura e um THMIEdit em Setpoint. Rode.

O mesmo em código, para quem cria os componentes em tempo de execução:

uses tcp_udpport, ISOTCPDriver, PLCTagNumber, ProtocolTypes;

var
  Port: TTCP_UDPPort;
  Drv: TISOTCPDriver;
  Temp: TPLCTagNumber;
begin
  Port := TTCP_UDPPort.Create(Self);
  Port.Host := '192.168.0.10';
  Port.Port := 102;

  Drv := TISOTCPDriver.Create(Self);
  Drv.CommunicationPort := Port;
  Drv.PLCRack := 0;
  Drv.PLCSlot := 0;

  Temp := TPLCTagNumber.Create(Self);
  Temp.MemReadFunction := 4;   // DB
  Temp.MemFile_DB := 1;
  Temp.MemAddress := 0;
  Temp.TagType := pttFloat;
  Temp.SwapBytes := True;
  Temp.SwapWords := True;
  Temp.RefreshTime := 500;
  Temp.ProtocolDriver := Drv;  // por último

  Port.Active := True;
end;

Outros exemplos no repositório: examples/laz_isotcp_demonstration (DB com bloco, string e bits), examples/laz_isotcp_hourmeter (horímetro com escrita), examples/laz_isotcp_performance (medição de taxa de atualização) e examples/demo_logo8.

Problemas comuns
Sintoma Causa provável
Porta abre (Active = True) mas nenhum tag atualiza; LastSyncReadStatus = ioConnectPLCFailed Rack/slot errados, ou S7-1200/1500 sem PUT/GET liberado, ou nível de proteção No access.
Porta não abre (OnCommPortOpenError) IP errado, porta 102 bloqueada por firewall, ou o CLP já atingiu o limite de conexões (troque ISOTCPConnType).
Tags de um DB específico dão ioIllegalMemoryAddress / ioObjectNotExists DB não existe, é menor que o endereço pedido, ou (1200/1500) ainda está com Optimized block access.
Valores de 16/32 bits absurdos (1 vira 256, REAL vira 1E-40) Falta SwapBytes/SwapWords.
Escritas falham com ioReadOnlyProtocol ou ioObjectAccessNotAllowed ReadOnly = True no driver, ou a CPU está com proteção de escrita/senha.
Funciona no S7-300 e não no LOGO! Force ForcePDUSize = pdu240 e confira o TSAP da conexão de servidor no LOGO!Soft.
Timers/contadores lidos como zero TagType precisa ser pttWord nessas áreas.
Exemplos relacionados

2 comentários em “Protocolo S7 sobre ISOTCP”

  1. Marcos Responder

    bom dia
    parabéns gostei muito, tenho uma pergunta
    este protocolo tem restrição de tamanho ou quantidade de variáveis
    pois tenho 6.000 variáveis e não estou conseguindo coletar todas

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *