Docs / Strand / tutorials/iot-alert-pipeline
Tutorial: IoT Alert Pipeline
Build an end-to-end pipeline that reads sensor data from an ESP32, sends it through Contact, analyzes it with AI in Strand, and delivers alerts to Slack.
ESP32 → Contact (MQTT) → Strand Workflow → AI Analysis → Slack Alert
Time: ~30 minutes Difficulty: Beginner You'll need: An ESP32 board, a Slack workspace, and an OpenAI or Anthropic API key
---
Step 1: Set Up Contact
Create a Service
In the Contact dashboard, go to Services and create a new service:
- Name:
sensor-monitoring - Validation Rules: (optional) Add a rule to check
temperatureis a number
Create an Entity
Go to Entities → Create Entity:
- Name:
esp32-sensor - Service:
sensor-monitoring
After creation, click Connect to get the MQTT credentials. Save these: you'll need the broker URL, username, and password.
Create an API Key
Go to Access Control → API Keys and create a key for esp32-sensor with the entity:WriteMessages permission. You'll use this in Strand to link the two platforms.
---
Step 2: Program the ESP32
Flash the following MicroPython code to your ESP32. This reads a temperature sensor and publishes data to Contact every 30 seconds.
from umqtt.simple import MQTTClient
# Wi-Fi (connect before this runs)
# Replace with your Contact MQTT credentials
BROKER = "mqtt.tendrl.com"
PORT = 443
CLIENT_ID = "your-account-number:us-1:entity:esp32-sensor" # your entity's resourcePath
USERNAME = "your-api-key-id"
PASSWORD = "your-api-key-secret"
# ADC pin for temperature sensor (adjust for your hardware)
adc = machine.ADC(machine.Pin(34))
adc.atten(machine.ADC.ATTN_11DB)
def read_temperature():
"""Convert ADC reading to Celsius (adjust for your sensor)."""
raw = adc.read()
voltage = raw * 3.3 / 4095
return round(voltage * 100, 1) # LM35 formula
client = MQTTClient(CLIENT_ID, BROKER, PORT, USERNAME, PASSWORD, ssl=True)
client.connect()
print("Connected to Contact MQTT broker")
while True:
temp = read_temperature()
payload = json.dumps({
"temperature": temp,
"unit": "celsius",
"device": CLIENT_ID
})
client.publish("contact/messages", payload)
print(f"Sent: {temp}°C")
time.sleep(30)
Don't have an ESP32? You can simulate messages with curl:
curl -X POST https://app.tendrl.com/api/entities/message \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"msg_type": "publish",
"data": {
"temperature": 28.5,
"unit": "celsius",
"device": "esp32-sensor"
}
}'
---
Step 3: Set Up Strand Connectors
You need three connectors. Go to the Strand Connectors page and create each one:
Slack Connector
| Field | Value |
|---|---|
| Type | Slack |
| Name | My Slack |
| Bot Token | Your Slack bot token (starts with xoxb-) |
| Default Channel | #sensor-alerts |
To get a Slack bot token: go to api.slack.com/apps, create an app, add chat:write and chat:write.public scopes under OAuth & Permissions, then install to your workspace. Copy the Bot User OAuth Token.
AI Connector (OpenAI or Anthropic)
| Field | Value |
|---|---|
| Type | OpenAI (or Anthropic) |
| Name | My AI |
| API Key | Your OpenAI/Anthropic API key |
Contact Message Connector
| Field | Value |
|---|---|
| Type | Contact Entity Message |
| Name | Contact Sensor |
| API Key | The entity API key from Step 1 |
---
Step 4: Create the Strand Workflow
Go to Flows → New Workflow and name it Sensor Alert Pipeline.
4a. Add the Trigger
Your workflow needs to receive events from Contact. Set up a Connector Subscription:
- Click Subscriptions in the workflow settings
- Add a subscription for your Contact Sensor connector
- This creates a trigger, so Contact will push messages to this workflow automatically
Alternatively, use the API trigger: call POST /trigger/{connector_id} with the sensor payload from any external system.
4b. Add a Python Snippet Node
Drag a Python Snippet node onto the canvas. This extracts the sensor data from Contact's message format:
# Sensor data from the Contact message
sensor_data = payload.get("data", {})
temperature = sensor_data.get("temperature", 0)
device = sensor_data.get("device", "unknown")
# Determine alert level
if temperature > 35:
alert_level = "critical"
elif temperature > 30:
alert_level = "warning"
else:
alert_level = "normal"
result = {
"temperature": temperature,
"device": device,
"alert_level": alert_level,
"needs_analysis": alert_level != "normal"
}
4c. Add a Logic Node (Filter)
Add a Logic node set to Filter mode. Connect it to the Python Snippet.
Condition: {{ steps.python_snippet.output_payload.needs_analysis }} == true
This ensures we only analyze and alert on abnormal readings, saving AI API costs.
4d. Add an AI Connector Node
Drag a Connector node and select your AI connector. Connect it after the Logic node.
Operation: chat
System Prompt:
You are an IoT monitoring assistant. Analyze sensor readings and provide a brief assessment (2-3 sentences) including: what the reading means, potential causes, and recommended action.
User Message:
Device: {{ steps.python_snippet.output_payload.device }}
Temperature: {{ steps.python_snippet.output_payload.temperature }}°C
Alert Level: {{ steps.python_snippet.output_payload.alert_level }}
4e. Add a Slack Connector Node
Drag another Connector node and select your Slack connector. Connect it after the AI node.
Operation: send_message
Message Text:
🌡️ *Sensor Alert: {{ steps.python_snippet.output_payload.alert_level | upper }}*
*Device:* {{ steps.python_snippet.output_payload.device }}
*Temperature:* {{ steps.python_snippet.output_payload.temperature }}°C
*AI Analysis:*
{{ steps.ai_node.output_payload.response }}
Final Workflow
Your workflow should look like this:
[Contact Trigger] → [Python Snippet] → [Logic Filter] → [AI Analysis] → [Slack Alert]
Click Deploy to activate the workflow.
---
Step 5: Test It
Option A: Send a Test Message from Contact
curl -X POST https://app.tendrl.com/api/entities/message \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"msg_type": "publish",
"data": {
"temperature": 36.2,
"unit": "celsius",
"device": "esp32-sensor"
}
}'
Option B: Trigger Strand Directly
curl -X POST https://app.tendrl.com/strand-api/trigger/YOUR_CONNECTOR_ID \
-H "Authorization: Bearer YOUR_STRAND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"payload": {
"data": {
"data": {
"temperature": 36.2,
"unit": "celsius",
"device": "esp32-sensor"
}
}
}
}'
Check your #sensor-alerts Slack channel; you should see an alert with AI analysis within a few seconds.
---
What's Happening
- ESP32 reads sensor data and publishes via MQTT to Contact
- Contact receives the message, validates it against the service rules, and forwards it to Strand via the connector subscription
- Strand runs the workflow:
- Extracts temperature and determines alert level
- Filters out normal readings
- Sends abnormal readings to AI for analysis
- Posts the analysis to Slack
- Slack delivers the alert to your team
The entire pipeline runs in seconds with zero custom backend code.
---
Next Steps
- Add more sensors: Create additional Contact entities and feed them into the same workflow
- Add email alerts: Use the Email connector for critical alerts
- Store readings: Use a Python Snippet to log data to a database
- Add thresholds: Use workflow variables to make temperature thresholds configurable
- Dashboard: Build a simple web app using Contact's REST API to display historical readings
Tendrl