Saltar al contenido principal

DOCUMENTACIÓN CON IA

¿Qué desea saber?

Conectar a Tulip

Tulip es una plataforma sin código de MES / operaciones conectadas en planta. La cámara OV10i se conecta a Tulip a través de Node-RED ejecutándose en la cámara — el mismo editor de flujos que utiliza para integraciones de PLC, MQTT y otras E/S. Tulip puede:

  • Activar una inspección desde un botón del operador o un evento de la aplicación.
  • Recibir los resultados aprobado/rechazado por ROI y globales más los metadatos de captura.
  • Cambiar la receta activa cuando el operador escanea el código de barras de una pieza o selecciona un producto diferente.
  • Mostrar la imagen en vivo (con superposiciones de cuadros delimitadores) dentro de una aplicación de Tulip para que el operador vea lo que vio la AI.

Esta guía recorre cada uno de esos cuatro puntos de integración de principio a fin.

Arquitectura

┌────────────────────────────┐               ┌──────────────────────────────┐
│ TULIP │ │ OV10i │
│ │ │ │
│ ┌──────────────────┐ │ HTTP/MQTT │ ┌──────────────────────┐ │
│ │ Operator Button │──────┼───trigger─────┼──▶│ Node-RED HTTP IN │──▶│
│ │ in Tulip App │ │ │ │ /trigger │ │
│ └──────────────────┘ │ │ └──────────────────────┘ │
│ │ │ │ │
│ ┌──────────────────┐ │ │ ▼ │
│ │ Tulip Connector │◀─────┼─── results ───┼─── All Block Outputs │
│ │ Function (HTTP) │ │ │ (image_url, pass/fail, │
│ └──────────────────┘ │ │ per-ROI classes) │
│ │ │ │ │
│ ┌──────────────────┐ │ │ ▼ │
│ │ Image Widget │◀─────┼── live-feed ──┼─── http://CAMERA_IP/live-feed│
│ │ (iframe URL) │ │ (HMI w/ ROI │ (shows bounding boxes │
│ └──────────────────┘ │ overlays) │ on the captured image) │
│ │ │ │
│ ┌──────────────────┐ │ │ ┌──────────────────────┐ │
│ │ Recipe Selector │──────┼─── HTTP ──────┼──▶│ /pipeline/activate │ │
│ │ (barcode / btn) │ │ │ │ {id: <recipe_id>} │ │
│ └──────────────────┘ │ │ └──────────────────────┘ │
└────────────────────────────┘ └──────────────────────────────┘

El puente en el medio siempre es Node-RED ejecutándose dentro del Bloque IO de la cámara — que es donde se construyen las cuatro integraciones.

Lienzo de Node-RED dentro del Bloque IO de la cámara — Modo Avanzado de la etapa de lógica de IO

Utilice variables de entorno para el endpoint de Tulip

Codificar de forma fija la URL del Connector de Tulip en el flujo de Node-RED ata el flujo a una sola implementación. Defina una variable de entorno TULIP_ENDPOINT en System Settings → Environment Variables, luego léala desde sus nodos HTTP Request con ${TULIP_ENDPOINT}. Esto le permite exportar el mismo flujo y reimportarlo en cada cámara de la línea. Consulte Environment Variables para el patrón completo.

Elija su transporte: HTTP o MQTT

OpciónCuándo usarlaNotas
HTTP (recomendado para la mayoría de las implementaciones de Tulip)Las Connector Functions de Tulip llaman de forma nativa a endpoints HTTP. La manera más sencilla de activar desde el botón de un widget de Tulip.Funciona sin infraestructura adicional. Tulip → cámara y cámara → Tulip usan ambos POST simple.
MQTTSu planta ya ejecuta un broker MQTT (Sparkplug B, broker IT/OT, etc.) y el Tulip Edge Connector está configurado para comunicarse con él.Menor latencia para señalización de alto volumen. Requiere un broker.
OPC UAEstá ejecutando una instancia de Tulip Edge MC con el OPC UA Connector.El OV10i viene con bloques de Node-RED para OPC UA preinstalados.

El resto de esta guía usa HTTP en los ejemplos — es lo que la mayoría de las implementaciones de Tulip utilizan por defecto. Los mismos patrones aplican para MQTT (reemplace los nodos HTTP IN / HTTP Request con nodos MQTT in / MQTT out).

1. Activar la cámara desde una aplicación de Tulip

Un operador de Tulip presiona un botón → la Connector Function de Tulip llama a un endpoint HTTP en el que el Node-RED de la cámara está escuchando → Node-RED activa una captura.

Lado de Node-RED: recibir el trigger

En el Bloque IO de la cámara (Modo Avanzado → Node-RED), construya:

[ HTTP IN: POST /trigger ]──▶[ Trigger node ]──▶[ HTTP Response: 200 ok ]
  1. Arrastre un nodo http in desde la categoría network.
    • Method: POST
    • URL: /trigger
  2. Conéctelo al nodo Trigger específico de OV (bajo block logic en la paleta de OV). El nodo Trigger dispara el pipeline de captura de la cámara.
  3. Agregue un nodo http response para que el Connector de Tulip reciba un 200 OK de inmediato (no haga esperar a Tulip por la inferencia de IA — deje que el resultado regrese de forma asíncrona, vea la Sección 2).
  4. Haga clic en Deploy.

El endpoint ahora está activo en http://<CAMERA_IP>/node-red/trigger (la raíz HTTP de Node-RED se monta bajo /node-red/).

Lado de Tulip: llamar al trigger desde un botón

En su cuenta de Tulip:

  1. Vaya a Apps → Connectors y cree un nuevo HTTP Connector si no tiene uno.
  2. Configure el Host con la IP de su cámara y el Port en 80 (o 443 si ha habilitado HTTPS).
  3. Cree una nueva Connector Function llamada Trigger Camera:
    • Method: POST
    • Endpoint: /node-red/trigger
    • No se necesita cuerpo ni parámetros de entrada para un trigger básico — la cámara usa la receta activa.
  4. Guarde y pruebe la Connector Function. Debería ver una respuesta 200 y la cámara debería capturar una imagen.
  5. En su aplicación de Tulip, agregue un widget Button. En su acción, elija Run Connector Function → Trigger Camera.

Cuando el operador toque el botón, la cámara se dispara.

Enviar contexto adicional con el trigger

Si desea enviar un número de serie o un ID de pieza con cada trigger, cambie la Connector Function de Tulip para aceptar entradas (por ejemplo, serial_number) y colóquelas en el cuerpo de la solicitud. Del lado de la cámara, su nodo HTTP IN las recibe como msg.payload y puede guardarlas en una variable de flujo para que el flujo de envío de resultados (Sección 2) pueda adjuntarlas al reporte de inspección.

2. Enviar resultados de regreso a Tulip

Después de que la cámara captura, el nodo All Block Outputs se activa con el resultado completo de la inspección. El patrón:

[ All Block Outputs ]──▶[ Function: format for Tulip ]──▶[ HTTP Request: POST to Tulip ]

Construir el flujo

  1. Conecte un nodo function después del nodo All Block Outputs.
  2. La función consolida los resultados por ROI en un único aprobado/rechazado global y recorta el payload a lo que Tulip realmente necesita:
// Read the camera's full inspection payload
const p = msg.payload || {};

// Consolidate per-ROI classifier predictions into a global pass/fail
const preds = p.classification?.predictions || [];
const allPass = preds.length > 0 && preds.every(r => r.predicted_class === 'pass');

// Optional: count segmentation defects (e.g., for thresholded fail)
const defectPixels = (p.segmentation?.blobs || [])
.reduce((s, b) => s + (b.pixel_count || 0), 0);

// Build a flat object for Tulip (Tulip Connector Functions like flat fields)
msg.payload = {
result: allPass ? "pass" : "fail",
inspection_id: p.inspection_id || null,
timestamp: new Date().toISOString(),
image_url: p.image_url || null, // see Section 4 for displaying this in Tulip
defect_pixel_count: defectPixels,
per_roi: preds.map(r => ({
roi: r.roi_name,
class: r.predicted_class,
confidence: r.confidence,
})),
// If you stashed a serial number from the trigger (see Section 1), add it:
// serial_number: flow.get('lastSerial') || null,
};

// Set headers for the outbound HTTP request
msg.headers = { 'Content-Type': 'application/json' };
msg.method = 'POST';
return msg;
  1. Conéctelo a un nodo http request:
    • Method: POST (o use msg.method desde la función anterior)
    • URL: ${TULIP_ENDPOINT} (leído desde la variable de entorno) o URL del Tulip Connector codificada de forma fija
    • Return: a UTF-8 string (o JSON si su endpoint de Tulip devuelve JSON)
  2. Conecte un nodo debug después de la solicitud HTTP para que pueda inspeccionar la respuesta de Tulip durante las pruebas.
  3. Haga clic en Deploy.

Lado de Tulip: recibir los resultados

En Tulip:

  1. Cree una HTTP Connector Function en la dirección de recepción — un endpoint webhook, o use la Tables API de Tulip para escribir los resultados en una Tabla de Tulip.
  2. Configure la lógica de su app de Tulip para reaccionar al payload entrante (por ejemplo, actualizar un indicador de estado, registrar en una Tabla, incrementar un contador).

Para Tulip Tables específicamente, el patrón es hacer POST a https://<your-tulip-instance>.tulip.co/api/v3/tables/<table_id>/records con los campos result, serial_number, timestamp. Use un token de API de Tulip en el header Authorization — almacene el token como credencial en el HTTP Connector, no en el flujo de Node-RED.

3. Cambiar recetas desde Tulip

Los operadores a menudo necesitan cambiar a una receta diferente cuando cambia el tipo de producto (escaneo de código de barras, dropdown, dispatch). La cámara expone un endpoint HTTP integrado que activa una receta por ID.

Encontrar el ID de la receta

  1. Abra la receta en el editor de recetas.
  2. Mire la barra de direcciones de su navegador: /recipes/15/editor → el ID es 15.

Construir el flujo de cambio de receta

[ HTTP IN: POST /change-recipe ]──▶[ Function: format request ]──▶[ HTTP Request: POST localhost:5001/pipeline/activate ]──▶[ HTTP Response ]
  1. Nodo HTTP IN: POST /change-recipe. Tulip lo llamará con un cuerpo como { "id": 15 }.
  2. Nodo Function — pasa el ID de la receta a la API interna de la cámara:
const recipeId = msg.payload?.id;
if (!recipeId) {
msg.statusCode = 400;
msg.payload = { error: "missing 'id' field" };
return msg;
}
msg.headers = { 'Content-Type': 'application/json' };
msg.payload = JSON.stringify({ id: recipeId });
msg.method = 'POST';
return msg;
  1. Nodo HTTP Request:
    • Method: POST
    • URL: localhost:5001/pipeline/activate
    • Return: a parsed JSON object
  2. Nodo HTTP Response para que la Connector Function de Tulip reciba un código de estado de vuelta.
URL de la API interna de la cámara

Dentro de Node-RED en la cámara, el endpoint de cambio de receta es localhost:5001/pipeline/activate — esa es la API administrativa interna de la cámara. Estás haciendo proxy de la solicitud de Tulip a través de Node-RED hacia la API local. No intentes llamar al puerto 5001 directamente desde fuera de la cámara; solo está expuesto a los flujos internos de la cámara.

Para firmware más antiguo (anterior a v18.92), la URL era http://[CAMERA_IP]/edge/pipeline/activate. Si está en firmware más antiguo y localhost:5001 no responde, recurra a la URL de v17/v18. Actualice el firmware de la cámara a v18.92+ para utilizar la ruta moderna.

Lado de Tulip: llamar al cambio de receta

  1. Agregue una Connector Function Switch Recipe en el mismo HTTP Connector.
  2. Method: POST, Endpoint: /node-red/change-recipe.
  3. Agregue un input parameter recipe_id (número).
  4. Establezca el cuerpo de la solicitud en { "id": $recipe_id }.
  5. En su aplicación de Tulip, conecte un disparador de Barcode Scanner o un widget Dropdown para llamar a esta función con el ID de receta correspondiente.

El operador escanea un código de barras → Tulip busca el ID de la receta para esa pieza → la Connector Function llama a la cámara → la cámara cambia de receta en menos de un segundo.

4. Mostrar la imagen en vivo (con cajas delimitadoras) en Tulip

El Image Widget de Tulip puede mostrar cualquier URL que devuelva una imagen. El OV10i tiene una URL HMI integrada que devuelve la captura más reciente con superposiciones de cajas delimitadoras y ROI dibujadas encima:

http://<CAMERA_IP>/live-feed

/live-feed mostrando la imagen de la cámara con superposición de ROI (cajas delimitadoras dibujadas)

Para incrustarla en una aplicación de Tulip:

  1. Agregue un Image Widget a su paso de Tulip.
  2. Establezca la Image URL en una variable de Tulip que se rellene a partir del resultado de la inspección, o en una URL estática si cada cámara tiene su propio paso de panel.
  3. El patrón más simple: codifique http://<CAMERA_IP>/live-feed y deje que se actualice automáticamente en cada captura (el image widget de Tulip se actualiza cuando su URL o un parámetro de consulta cambia — agregue ?t={{timestamp}} para forzar la actualización).

Si desea la captura en bruto (sin cajas delimitadoras), utilice image_url desde el msg.payload en su lugar — esa es una URL por captura que apunta a la imagen guardada en la Biblioteca de la cámara.

Un paso de Tulip, múltiples cámaras

Si tiene varias cámaras en la misma línea, cada una con su propia IP, almacene la URL de la cámara en una variable de Tulip por paso de la aplicación y deje que el operador elija qué estación ver. El Image Widget cambia instantáneamente cuando la variable cambia.

Flujo de referencia (JSON de Node-RED importable)

El flujo a continuación combina las cuatro integraciones: HTTP IN de activación, HTTP Request para envío de resultados, cambiador de recetas y un nodo de depuración. Copie el JSON, abra Node-RED → menú hamburguesa → Import, y pegue.

[
{
"id": "tulip_trigger_in",
"type": "http in",
"name": "POST /trigger",
"url": "/trigger",
"method": "post",
"x": 160, "y": 100, "wires": [["tulip_trigger_fire"]]
},
{
"id": "tulip_trigger_fire",
"type": "trigger-camera",
"name": "Trigger camera",
"x": 380, "y": 100, "wires": [["tulip_trigger_resp"]]
},
{
"id": "tulip_trigger_resp",
"type": "http response",
"name": "200 ok",
"statusCode": "200",
"x": 580, "y": 100, "wires": []
},
{
"id": "tulip_results_format",
"type": "function",
"name": "Format for Tulip",
"func": "const p = msg.payload || {};\nconst preds = p.classification?.predictions || [];\nconst allPass = preds.length > 0 && preds.every(r => r.predicted_class === 'pass');\nmsg.payload = {\n result: allPass ? 'pass' : 'fail',\n timestamp: new Date().toISOString(),\n image_url: p.image_url || null,\n per_roi: preds.map(r => ({roi: r.roi_name, class: r.predicted_class, confidence: r.confidence}))\n};\nmsg.headers = {'Content-Type':'application/json'};\nmsg.method = 'POST';\nreturn msg;",
"x": 380, "y": 200, "wires": [["tulip_results_send"]]
},
{
"id": "tulip_results_send",
"type": "http request",
"name": "POST to Tulip",
"method": "POST",
"url": "${TULIP_ENDPOINT}",
"ret": "obj",
"x": 600, "y": 200, "wires": [["tulip_results_debug"]]
},
{
"id": "tulip_results_debug",
"type": "debug",
"name": "Tulip response",
"x": 800, "y": 200, "wires": []
},
{
"id": "tulip_recipe_in",
"type": "http in",
"name": "POST /change-recipe",
"url": "/change-recipe",
"method": "post",
"x": 160, "y": 300, "wires": [["tulip_recipe_format"]]
},
{
"id": "tulip_recipe_format",
"type": "function",
"name": "Format request",
"func": "const id = msg.payload?.id;\nif (!id) { msg.statusCode = 400; msg.payload = {error:'missing id'}; return msg; }\nmsg.headers = {'Content-Type':'application/json'};\nmsg.payload = JSON.stringify({id});\nmsg.method = 'POST';\nreturn msg;",
"x": 380, "y": 300, "wires": [["tulip_recipe_call"]]
},
{
"id": "tulip_recipe_call",
"type": "http request",
"name": "/pipeline/activate",
"method": "POST",
"url": "localhost:5001/pipeline/activate",
"ret": "obj",
"x": 600, "y": 300, "wires": [["tulip_recipe_resp"]]
},
{
"id": "tulip_recipe_resp",
"type": "http response",
"name": "to Tulip",
"x": 800, "y": 300, "wires": []
}
]

Aún necesitará conectar el nodo All Block Outputs a tulip_results_format (no está en el JSON anterior porque ese nodo es específico de OV y ya existe en cada flujo).

Solución de problemas

SíntomaCausa / solución
El Tulip Connector devuelve connection refusedLa cámara y el Tulip Edge Device no están en la misma red, o el firewall de la cámara está bloqueando el puerto 80. Ambos deben ser accesibles entre sí; primero haga ping a la cámara desde el Tulip Edge Device.
El HTTP IN de activación nunca se disparaOlvidó hacer Deploy en Node-RED después de agregar el nodo, o la URL tiene un error tipográfico (debe comenzar con /, no con /node-red/).
El cambio de receta devuelve 404Endpoint incorrecto para su firmware. Pre-v18.92 usa /edge/pipeline/activate desde la IP de la cámara; v18.92+ usa localhost:5001/pipeline/activate desde dentro de Node-RED.
/live-feed muestra una imagen negra en TulipLa cámara aún no ha tomado una captura, o la imagen no se ha actualizado. Agregue ?t={{Date.now()}} para forzar al Image Widget a recargar.
Tulip recibe un 200 pero sin cuerpo de resultadoLa cámara dispara el trigger e inmediatamente devuelve 200 — el resultado regresa de forma asíncrona vía el flujo de envío de resultados (Sección 2). Asegúrese de que ese flujo esté conectado y desplegado.
Los resultados por ROI son ruidosos / Tulip solo quiere aprobado/rechazadoPor eso el nodo Function consolida todo en un solo campo `result: "pass"

Qué sigue