From gcp-iot
This skill provides guidance on GCP IoT architecture patterns and best practices. Use this skill when working with ESP32 sensors, Cloud Run backends, Pub/Sub messaging, or Firebase frontends. Triggers when user asks about "IoT architecture", "GCP best practices", "sensor to cloud", "telemetry patterns", "realtime IoT", or needs guidance on designing IoT systems on Google Cloud.
How this skill is triggered — by the user, by Claude, or both
Slash command
/gcp-iot:gcp-iot-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Google Cloud Platform │
│ │
│ ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ ESP32 │ ───► │ Cloud Run │ ───► │ Pub/Sub │ │
│ │ Sensors │ HTTPS│ API Gateway │ │ Topic │ │
│ └─────────┘ └─────────────┘ └──────┬──────┘ │
│ │ │
│ ┌────────────────────────────┼────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ Firestore │ │ Push to │ │ BigQuery │ │
│ │ Database │ │ WebSocket │ │ Analytics │ │
│ └─────────────┘ └──────┬──────┘ └───────────┘ │
│ │ │ │
│ └──────────────────────────┼────────────────────────┤
│ │ │
└───────────────────────────────────────────────┼────────────────────────┘
│
▼
┌─────────────┐
│ React │
│ Frontend │
│ (Firebase) │
└─────────────┘
Flow: ESP32 → Cloud Run → Pub/Sub (Push) → WebSocket Service → Frontend
Pros:
Cons:
Implementation:
// Cloud Run: Receive telemetry and publish
app.post('/api/telemetry', async (req, res) => {
const { deviceId, readings } = req.body;
await pubsub.topic('telemetry').publish(
Buffer.from(JSON.stringify({ deviceId, readings, timestamp: Date.now() }))
);
res.status(200).json({ status: 'received' });
});
Flow: ESP32 → Cloud Run → Pub/Sub → Cloud Functions → Multiple Destinations
Use When:
// Cloud Function: Process and fan-out
exports.processTelemetry = async (message) => {
const data = JSON.parse(Buffer.from(message.data, 'base64'));
// Store in Firestore
await db.collection('telemetry').add(data);
// Update device status
await db.collection('devices').doc(data.deviceId).set({
lastSeen: FieldValue.serverTimestamp(),
status: 'online'
}, { merge: true });
// Publish to frontend topic
await pubsub.topic('frontend-updates').publish(
Buffer.from(JSON.stringify(data))
);
};
Flow: ESP32 → Cloud Run → Pub/Sub → Dataflow → BigQuery + Firestore
Use When:
// ESP32: Send heartbeat every 30 seconds
void loop() {
if (millis() - lastHeartbeat > 30000) {
sendHeartbeat();
lastHeartbeat = millis();
}
}
// Cloud Function: Mark offline if no heartbeat
exports.checkDeviceStatus = functions.pubsub
.schedule('every 1 minutes')
.onRun(async () => {
const cutoff = Date.now() - 60000; // 1 minute
const devices = await db.collection('devices')
.where('lastSeen', '<', new Date(cutoff))
.where('status', '==', 'online')
.get();
const batch = db.batch();
devices.forEach(doc => {
batch.update(doc.ref, { status: 'offline' });
});
await batch.commit();
});
Store expected disconnect behavior:
// On device connect
await db.collection('devices').doc(deviceId).set({
status: 'online',
lastWill: 'offline'
}, { merge: true });
# Create telemetry topic
gcloud pubsub topics create telemetry --project=$PROJECT_ID
# Create push subscription for websocket
gcloud pubsub subscriptions create telemetry-ws-push \
--topic=telemetry \
--push-endpoint=https://ws-service.run.app/pubsub \
--push-auth-service-account=push-invoker@$PROJECT_ID.iam.gserviceaccount.com \
--ack-deadline=10 \
--project=$PROJECT_ID
# Create pull subscription for processing
gcloud pubsub subscriptions create telemetry-process \
--topic=telemetry \
--ack-deadline=60 \
--project=$PROJECT_ID
# Create dead letter topic
gcloud pubsub topics create telemetry-dead-letter
# Update subscription with dead letter
gcloud pubsub subscriptions update telemetry-ws-push \
--dead-letter-topic=telemetry-dead-letter \
--max-delivery-attempts=5
# service.yaml
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/minScale: "1" # Avoid cold starts
autoscaling.knative.dev/maxScale: "10"
spec:
containerConcurrency: 80
timeoutSeconds: 60
containers:
- resources:
limits:
memory: 512Mi
cpu: "1"
# Enable session affinity for socket.io
gcloud run services update ws-service \
--session-affinity \
--min-instances=1
// ESP32: Include API key in header
http.addHeader("X-API-Key", API_KEY);
http.addHeader("X-Device-ID", DEVICE_ID);
// Cloud Run: Validate device
app.use('/api/telemetry', (req, res, next) => {
const apiKey = req.headers['x-api-key'];
const deviceId = req.headers['x-device-id'];
if (!validateDevice(apiKey, deviceId)) {
return res.status(401).json({ error: 'Unauthorized device' });
}
next();
});
# Store secrets
echo -n "my-api-key" | gcloud secrets create api-key --data-file=-
# Access in Cloud Run
gcloud run services update my-service \
--set-secrets=API_KEY=api-key:latest
| Component | Free Tier | Optimization |
|---|---|---|
| Cloud Run | 2M requests/mo | Batch telemetry, use min-instances=0 for dev |
| Pub/Sub | 10GB/mo | Compress messages, batch publishes |
| Firestore | 50K reads/day | Cache reads, batch writes |
| Functions | 2M invocations/mo | Use Cloud Run for high-volume |
# Create uptime check
gcloud monitoring uptime-check-configs create api-health \
--display-name="IoT API Health" \
--http-check=request-method=GET,path=/health,use-ssl=true \
--monitored-resource=gce_instance
# Create alert for errors
gcloud alpha monitoring policies create \
--notification-channels=[CHANNEL] \
--condition-display-name="High Error Rate" \
--condition-filter='resource.type="cloud_run_revision" AND metric.type="run.googleapis.com/request_count" AND metric.labels.response_code_class="5xx"'
{
"deviceId": "esp32-001",
"timestamp": 1705337600000,
"type": "telemetry",
"readings": {
"temperature": 25.5,
"humidity": 60.2,
"pressure": 1013.25
},
"meta": {
"firmware": "1.2.0",
"rssi": -65,
"battery": 85,
"uptime": 3600000
}
}
/devices/{deviceId}
- status: "online" | "offline"
- lastSeen: Timestamp
- firmware: "1.2.0"
- config: { ... }
/devices/{deviceId}/telemetry/{timestamp}
- readings: { temperature, humidity, ... }
- meta: { rssi, battery, ... }
❌ Don't: Poll from frontend every second ✅ Do: Use realtime listeners (Firestore onSnapshot, WebSocket)
❌ Don't: Store every reading in Firestore ✅ Do: Aggregate/downsample for long-term storage
❌ Don't: Use single Cloud Run instance for everything ✅ Do: Separate API gateway from WebSocket service
❌ Don't: Hardcode credentials in ESP32 ✅ Do: Use secure provisioning and API keys
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub maxcogar/agent-armory --plugin gcp-iot