A contagem de produção com sensores IoT substitui planilhas de apontamento e estimativas de fim de turno por contagens de peças exatas e ao vivo. Neste tutorial você vai contar peças boas e rejeitadas com sensores fotoelétricos e um Raspberry Pi, fazer o contador sobreviver a reinícios e quedas de rede e publicar totais de turno retidos via MQTT, para que todo painel, tablet e relatório mostre o mesmo número.
Por que a contagem automática importa
Contagens escritas à mão chegam atrasadas, arredondadas e às vezes otimistas. Elas aparecem depois do turno, quando já não há o que fazer com um déficit. Um contador automático mostra a diferença para a meta enquanto ainda há tempo de reagir e fornece as entradas de qualidade e desempenho de que o OEE precisa. Também é um dos projetos IoT mais simples do chão de fábrica: um sensor, um dispositivo de borda, um topic.
Escolhendo o ponto de contagem
| Ponto de contagem | Prós | Contras |
|---|---|---|
| Contador de peças do CLP ou bit de "peça concluída" | Exato, já com debounce | Exige acesso e alterações no CLP |
| Sensor fotoelétrico na saída da estação | Retrofit sem mexer no CLP; funciona na maioria das peças | Precisa lidar com espaçamentos, peças encostadas, superfícies reflexivas |
| Sensor indutivo | Robusto para peças metálicas, ignora sujeira | Alcance curto, apenas metal |
| Sensor na calha de rejeito ou caçamba de refugo | Fornece a contagem de refugo para a taxa de qualidade | Exige um segundo sensor |
| Estação de embalagem (caixas, depois multiplicar) | Conta o que é realmente expedido | Atrasado, esconde refugo entre a estação e a embalagem |
Conte o mais perto possível do ponto em que a peça se torna "boa". Se a linha tiver uma estação de inspeção, conte as peças aprovadas depois dela e as rejeitadas no caminho de rejeito. A contagem de boas somada à de refugo dá a produção total.
Entre os sensores fotoelétricos, os do tipo barreira são os mais confiáveis em esteiras, porque peças encostadas ou brilhantes podem confundir sensores difusos. Monte o sensor de modo que cada peça interrompa o feixe exatamente uma vez, com um espaço entre as peças.
Ligações
Sensores industriais operam em 24 V e chaveiam saídas PNP ou NPN. Ligue-os ao Raspberry Pi por uma placa de entradas com optoacopladores; os pinos GPIO do Pi toleram apenas 3,3 V. Exemplo de atribuição:
| Entrada | GPIO (BCM) | Sinal |
|---|---|---|
| Sensor de peça boa | 17 | Saída do receptor de barreira |
| Sensor de rejeito | 27 | Sensor da calha de rejeito |
| Reset de turno (opcional) | 22 | Chave com trava ou botão do supervisor |
Design de topics para contagens e totais de turno
connectmq/plant1/packaging/line2/counter/count every N seconds, not retained
connectmq/plant1/packaging/line2/counter/shift retained shift totals
connectmq/plant1/packaging/line2/counter/status retained online / offline
A mensagem shift é retida, então um dashboard aberto no meio do turno mostra imediatamente os totais atuais. Seu payload contém totais absolutos, não incrementos:
{
"shift": "2026-10-16-A",
"start": 1760594400,
"good": 1843,
"reject": 27,
"target": 2400,
"ts": 1760600000
}
Publicar totais absolutos é a decisão de projeto principal. Se uma mensagem for perdida ou entregue duas vezes, a próxima ainda contém o total correto; incrementos acumulariam erro.
O script de borda para contagem de produção com IoT
O script conta as bordas nas duas entradas, salva os totais em um pequeno arquivo a cada poucos segundos para que um reboot não os perca, detecta trocas de turno a partir de uma escala e publica os totais retidos. Instale as dependências em um ambiente virtual com pip install gpiozero lgpio paho-mqtt.
#!/usr/bin/env python3
"""Production counting IoT edge: sensors -> persistent counts -> retained MQTT totals."""
import json
import os
import threading
import time
from datetime import datetime, timedelta
import paho.mqtt.client as mqtt
from gpiozero import DigitalInputDevice
BROKER = "broker.connectmq.com"
BASE = "connectmq/demo/pc-9d1x/line2/counter"
STATE_FILE = "/var/lib/pc-counter/state.json"
PUBLISH_EVERY = 5 # segundos
SHIFTS = [("A", 6), ("B", 14), ("C", 22)]
TARGET_PER_SHIFT = 2400
lock = threading.Lock()
def current_shift(now=None):
now = now or datetime.now()
starts = []
for name, hour in SHIFTS:
start = now.replace(hour=hour, minute=0, second=0, microsecond=0)
if start > now:
start -= timedelta(days=1)
starts.append((start, name))
start, name = max(starts)
return f"{start:%Y-%m-%d}-{name}", int(start.timestamp())
def load_state():
shift_id, start = current_shift()
try:
with open(STATE_FILE) as f:
state = json.load(f)
if state.get("shift") == shift_id:
return state
except (FileNotFoundError, ValueError):
pass
return {"shift": shift_id, "start": start, "good": 0, "reject": 0}
def save_state(state):
tmp = STATE_FILE + ".tmp"
with open(tmp, "w") as f:
json.dump(state, f)
os.replace(tmp, STATE_FILE) # atômico no mesmo sistema de arquivos
state = load_state()
def count(field):
def handler():
with lock:
state[field] += 1
return handler
good_in = DigitalInputDevice(17, pull_up=True, bounce_time=0.01)
reject_in = DigitalInputDevice(27, pull_up=True, bounce_time=0.01)
good_in.when_activated = count("good")
reject_in.when_activated = count("reject")
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
client.publish(f"{BASE}/status", "online", qos=1, retain=True)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="pc-9d1x-line2")
client.will_set(f"{BASE}/status", "offline", qos=1, retain=True)
client.on_connect = on_connect
client.connect(BROKER, 1883, keepalive=30)
client.loop_start()
os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
while True:
time.sleep(PUBLISH_EVERY)
shift_id, start = current_shift()
with lock:
if shift_id != state["shift"]:
final = dict(state, ts=int(time.time()), final=True)
client.publish(f"{BASE}/shift-closed", json.dumps(final), qos=1)
state.update(shift=shift_id, start=start, good=0, reject=0)
snapshot = dict(state, target=TARGET_PER_SHIFT, ts=int(time.time()))
save_state(state)
client.publish(f"{BASE}/shift", json.dumps(snapshot), qos=1, retain=True)
client.publish(f"{BASE}/count", json.dumps(snapshot), qos=0)
Pontos que merecem destaque:
- Gravação atômica: escrever em um arquivo temporário e renomeá-lo significa que uma queda de energia durante a gravação não corrompe o arquivo de estado.
- Troca de turno na borda: o próprio dispositivo fecha o turno e publica um registro final em
shift-closed, então o total fica correto mesmo que nenhum servidor esteja ouvindo naquele momento. Um historiador assinado com sessão persistente o recebe depois. - Debounce:
bounce_timefiltra ruído elétrico; peças que passam mais rápido que a janela de debounce seriam subcontadas, então defina-o a partir da sua taxa máxima de peças.
Crie /var/lib/pc-counter com permissões para o usuário do serviço e rode o script sob o systemd.
Acompanhando as contagens
mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-view-$RANDOM \
-t "connectmq/demo/pc-9d1x/line2/counter/#" -v
O total retido do turno e o status chegam primeiro, depois as atualizações a cada cinco segundos. No servidor MQTT público qualquer pessoa pode ler esses topics, o que não é problema em um teste de bancada passando a mão por uma barreira de luz, mas não serve para uma linha real. Em produção, adicione client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD") e client.tls_set(), use o client ID emitido e conecte na porta 8883.
Validando o contador
Antes que alguém confie nos números, verifique-os:
- Rode um lote conhecido (por exemplo 200 peças contadas à mão) e compare.
- Teste com peças encostadas umas nas outras e com a maior velocidade de linha realista.
- Verifique o comportamento em um reboot no meio de uma produção: a contagem precisa continuar, não recomeçar do zero.
- Desconecte a rede por alguns minutos: a contagem continua localmente, e o próximo total publicado inclui as peças feitas enquanto estava offline.
- Compare um turno completo com a contagem da embalagem ou do ERP; as diferenças devem ser explicáveis (refugo, retrabalho, WIP).
Problemas comuns
| Problema | Causa | Correção |
|---|---|---|
| Contagens duplicadas | Bordas ou furos da peça interrompem o feixe duas vezes | Aumente o debounce, reposicione o sensor, use barreira |
| Contagens perdidas | Peças encostadas, debounce longo demais | Crie espaçamento na esteira, encurte o debounce, use o sinal do CLP |
| Contagem zerada após queda de energia | Estado não persistido | Persista em arquivo como mostrado, ou use o contador do CLP |
| Dashboards mostram o turno de ontem | Mensagem retida de um dispositivo parado | Monitore o status; mostre "offline" com destaque |
| Totais diferentes entre telas | Telas calculam a partir de incrementos | Exiba sempre o total absoluto retido |
Exibindo as contagens na linha
A exibição mais eficaz é a mais simples: peças boas até agora, meta até agora (proporcional ao tempo decorrido do turno) e a diferença, em dígitos grandes visíveis do posto de trabalho. Um navegador em uma tela de parede pode assinar via WebSocket seguro o topic retido shift e calcular a meta proporcional a partir de start e target, sem precisar de servidor extra. Pinte a diferença de verde quando estiver adiantado e de vermelho quando estiver atrasado, e mostre a contagem de rejeitos ao lado, para que a velocidade nunca seja celebrada às custas da qualidade.
Durante o comissionamento, o explorador de topics ao vivo no portal ConnectMQ mostra o total retido e o status de cada contador, o que facilita conferir se todas as linhas estão publicando antes de as telas serem instaladas.
Usando as contagens
Contagens ao vivo versus meta são o painel motivacional mais simples em uma linha. Combinadas com o tempo de operação do monitoramento de status de máquinas e com os dados de ciclo do monitoramento de tempo de ciclo, elas alimentam diretamente o monitoramento de OEE em tempo real: contagem de boas sobre contagem total é a taxa de qualidade, e a contagem total contra o tempo de ciclo ideal dá o desempenho. As equipes de energia usam a mesma contagem de peças boas para calcular a energia por peça.
O broker é a fonte única de verdade para todos esses consumidores, e é por isso que vale entender o que um broker MQTT faz antes de escalar para muitas linhas. O guia de manufatura inteligente mostra onde a contagem se encaixa em um roadmap mais amplo, incluindo o gêmeo digital da linha.
Escale seus contadores no ConnectMQ
O ConnectMQ é um broker MQTT gerenciado com TLS, MQTT 5, retained messages, uma visão de dispositivos ISA-95 e alertas por limite, por exemplo quando a contagem good fica atrás do planejado. Crie um broker Serverless gratuito sem cartão de crédito e conecte seu primeiro contador em minutos.
Perguntas frequentes
Um contador de produção deve publicar incrementos ou totais?
Totais. Um total absoluto do turno em uma mensagem retida fica correto mesmo que mensagens sejam perdidas ou duplicadas, e novos subscribers veem o valor atual imediatamente.
O que acontece com as contagens se a rede cair?
O dispositivo de borda continua contando localmente e persiste os totais. Quando a conexão volta, a próxima mensagem publicada contém o total completo, incluindo as peças feitas durante a queda.
Qual é o melhor sensor para projetos de contagem de produção com IoT?
Sensores fotoelétricos do tipo barreira são a escolha geral mais confiável em esteiras. Sensores indutivos servem para peças metálicas, e um sinal de peça concluída do CLP é o melhor quando disponível.
Como contar o refugo separadamente?
Adicione um segundo sensor no caminho de rejeito ou use o sinal de rejeito da estação de inspeção, e publique as contagens de boas e de rejeitadas na mesma mensagem para que a taxa de qualidade seja sempre consistente.



