ormus-solutions/libreembed-claude-code/iot-protocols

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

IoT protocol reference: selection matrix, MQTT QoS flows and LWT, topic design, CoAP observe and block-wise transfer, LwM2M objects and firmware update, BLE GATT and connection tuning, LoRaWAN airtime and classes, and Paho, Zephyr, and ESP-IDF client code. Use when designing or debugging the protocol layer of a connected device.

Install

npx vskill@latest install ormus-solutions/libreembed-claude-code/iot-protocols

SKILL.md

IoT protocols pattern library

Reference patterns for MQTT, CoAP, LwM2M, BLE GATT, LoRaWAN, Thread, and Zigbee.

Protocol selection matrix

ConstraintMQTT (TCP)CoAP (UDP)LoRaWANBLE GATTNB-IoT / LTE-MThread
10-year battery targetNoMaybeYesYes (peripheral)MaybeMaybe
Internet reachYesYesYes (via gateway)NoYesMaybe (via border router)
> 100 m rangeYes (via WiFi)Yes (via WiFi)Yes (up to 10 km)NoYesYes (mesh)
> 1 kbps throughputYesYesNoYesYesYes
At-least-once deliveryYes (QoS ≥ 1)Yes (CON)Yes (confirmed)Yes (indications)YesYes
Standard cloud ecosystemYesYes (less mature)Yes (TTN, etc.)NoYesMaybe

MQTT state machines

QoS 1 publish flow
PUBLISHER BROKER SUBSCRIBER
| | |
|─ PUBLISH (msg_id=N) ────→| |
| |─ PUBLISH (msg_id=M) ───→|
| |←── PUBACK (M) ──────────|
|←─ PUBACK (N) ────────────| |

Retransmit logic:

  • If PUBACK not received within timeout (default 5-30s), publisher retransmits with DUP=1.
  • Subscriber may receive duplicate; deduplicate on app-side via msg_id.
QoS 2 publish flow
PUBLISHER BROKER
| |
|─ PUBLISH (id=N) ────────→|
|←─ PUBREC (N) ────────────|
|─ PUBREL (N) ────────────→|
|←─ PUBCOMP (N) ───────────|

State persisted at publisher between PUBLISH and PUBCOMP. Retransmit PUBLISH if PUBREC times out; retransmit PUBREL if PUBCOMP times out.

LWT setup

In the CONNECT packet:

will_topic: /devices/${id}/status
will_payload: {"online": false}
will_qos: 1
will_retain: true

When broker detects ungraceful disconnect (TCP RST, keep-alive timeout), it publishes the will message. Subscribers see "device offline" without explicit signaling.

Topic design

Hierarchical with predictable structure:

/{tenant}/{device_class}/{device_id}/{stream}

Examples:

  • /acme/sensor/dev-abc-123/temperature
  • /acme/sensor/dev-abc-123/cmd/+ (subscribe with wildcard)
  • /acme/+/+/alerts (subscribe to all alerts across all device types)

Wildcards:

  • +: matches exactly one level
  • #: matches all remaining levels (must be last)

CoAP semantics

Observe pattern
Client Server
| |
|─ GET /sensor Observe: 0 ─────────→|
|←─ 2.05 Content (value=23) ────────| (current value, Observe: N)
| |
| ... time passes, value changes ...
| |
|←─ 2.05 Content (value=25) ────────| (notification, Observe: N+1)
| |
|─ GET /sensor Observe: 1 ─────────→| (deregister)
|←─ 2.05 Content (last value) ──────|

Server-side state: list of observers per resource. Each notification increments the Observe number.

Block-wise transfer

For payloads > MTU (~ 1100 bytes over Ethernet, less over LoRaWAN):

Request blocks 0, 1, 2, ... until M (more-blocks) flag is 0.

GET /firmware Block2: 0/0/512 ─→ request first 512-byte block
2.05 Content Block2: 0/1/512 ←─ block 0, more=1, size=512
GET /firmware Block2: 1/0/512 ─→ next
2.05 Content Block2: 1/1/512 ←─ block 1, more=1
...
GET /firmware Block2: N/0/512 ─→ last
2.05 Content Block2: N/0/512 ←─ block N, more=0 (done)

LwM2M object model

Standard objects (URN style: /object_id/instance_id/resource_id):

/3 Device
/3/0/0 Manufacturer
/3/0/1 Model number
/3/0/2 Serial number
/3/0/3 Firmware version
/3/0/9 Battery level
/5 Firmware Update
/5/0/0 Package (or)
/5/0/1 Package URI
/5/0/2 Update (execute)
/5/0/3 State (idle/downloading/downloaded/updating)
/5/0/5 Update Result
/3303 Temperature
/3303/0/5700 Sensor Value
/3303/0/5601 Min Measured
/3303/0/5602 Max Measured
/3303/0/5603 Min Range
/3303/0/5604 Max Range
/3303/0/5701 Sensor Units

OMA LwM2M standardizes the IDs. Custom objects use object IDs ≥ 26241 (vendor range).

Firmware update flow
1. Server: PUT /5/0/1 with firmware URL
2. Device: state /5/0/3 transitions Idle → Downloading
3. Device: HTTP/CoAP/MQTT download of firmware
4. Device: state /5/0/3 transitions Downloading → Downloaded
5. Server: POST /5/0/2 (execute Update)
6. Device: state /5/0/3 transitions Downloaded → Updating
7. Device: applies update, reboots
8. Device: re-registers, /5/0/3 = Idle, /5/0/5 = Success
9. Device: /3/0/3 (firmware version) now reflects new version

If anything fails, /5/0/5 (Update Result) is set to the appropriate code (1=success, 2=insufficient storage, 3=insufficient memory, 4=connection lost, 5=integrity check failure, 6=unsupported package type, 7=invalid URI, 8=firmware update failed, 9=unsupported protocol).

BLE GATT design

Service + characteristic tree
Service (UUID: 0xFEED, vendor-defined)
├─ Characteristic A (UUID: 0xFEE1, read/notify)
│ └─ CCCD descriptor (0x2902) — enables notifications
├─ Characteristic B (UUID: 0xFEE2, write)
└─ Characteristic C (UUID: 0xFEE3, read)

Standard services (use when possible — phones auto-recognize):

UUIDService
0x180ADevice Information
0x180FBattery
0x180DHeart Rate
0x1812HID
0x181CUser Data
0x181DWeight Scale
Connection parameter tuning

For power on the peripheral:

ParamLow powerLow latency
Min connection interval1000 ms7.5 ms
Max connection interval2000 ms15 ms
Slave latency40
Supervision timeout6000 ms1000 ms

iOS overrides these. Aim for 30-50 ms connection interval if iOS support matters.

MTU negotiation

Default MTU: 23 bytes (20 bytes payload after ATT overhead).

Negotiated MTU: up to 247 (most stacks) or 517 (BLE 5.x).

Larger MTU = fewer fragments = lower latency for large transfers. Request MTU during connection setup.

LoRaWAN bandwidth planning

Air time table (EU 868 MHz, SF7-SF12)

For a 13-byte application payload (26 bytes on air after the 13-byte LoRaWAN header and MIC), 125 kHz, coding rate 4/5, 8-symbol preamble, low data rate optimization on at SF11 and SF12:

SFTime on airSensitivityRange
762 ms-123 dBm~2 km open field
8113 ms-126 dBm~3 km
9206 ms-129 dBm~5 km
10412 ms-132 dBm~7 km
11823 ms-134.5 dBm~10 km
121647 ms-137 dBm~15 km

EU 868 1% duty cycle allows 36 s of airtime per hour. At SF12 that is 36 / 1.647 s ≈ 21 uplinks per hour, at most one every ~163 seconds (99 × the airtime off after each uplink). ADR is essential.

Class trade-offs
ClassDownlink latencyPowerUse case
AUp to next uplinkLowestSensors, infrequent transmitters
BUp to beacon period (e.g., 128 s)MediumScheduled control
C< 1 secondHighestActuators, always-on receive

Common mistakes catalog

"MQTT client reconnects every few minutes"

NAT timeout. The router between the device and broker closes the TCP connection after idle period. Solutions:

  • Reduce MQTT keep-alive below the NAT timeout (often 60s is safe)
  • Use TCP keep-alive at OS level
"BLE device disconnects randomly"

Supervision timeout too short, or peer requesting incompatible connection params. Check:

  • Supervision timeout ≥ (1 + slave_latency) × max_conn_interval × 4
  • Peer (e.g., iOS) accepts your requested intervals
"LoRaWAN device works in lab, fails in field"

Lab probably had clear line of sight to gateway. Field has obstacles. ADR will adapt SF up but may not be enough; check signal margin and consider repositioning gateway.

"Device occasionally sends garbled payloads"

CRC isn't catching corruption. Check:

  • Stack handles all framing errors (UART parity / CAN CRC / radio CRC)
  • Payload is properly framed at application layer
  • Endianness is consistent across MCU + cloud
"Cloud sees device as offline immediately after deploy"

LWT was published because graceful disconnect didn't happen. Common causes:

  • Power glitch during firmware boot
  • Bug in disconnect sequence
  • Reset before MQTT graceful disconnect
"Battery drops to 80% on Day 1 then stable"

Lithium battery passivation layer. Not a real drain — the chemistry recovers. Expect this.

Code patterns

IoT protocol patterns for constrained MCUs and embedded Linux.

MQTT Topic Structure

Well-structured topic hierarchy enables flexible filtering and routing:

device/{device_id}/telemetry ← sensor data (QoS 0, no retain)
device/{device_id}/status ← online/offline (QoS 1, retained)
device/{device_id}/cmd ← commands to device (QoS 1)
device/{device_id}/cmd/ack ← command acknowledgment
device/{device_id}/ota/cmd ← OTA start/control
device/{device_id}/ota/data ← firmware chunks
fleet/+/telemetry ← subscribe to all device telemetry

LWT (Last Will and Testament) configuration:

c
esp_mqtt_client_config_t cfg = {
.session.last_will = {
.topic = "device/" DEVICE_ID "/status",
.msg = "{\"online\":false}",
.qos = 1,
.retain = true, /* Retained: new subscribers see last status immediately */
},
};

On connect, publish: device/{id}/status = {"online":true} retained.

Paho MQTT C Client (Linux/RTOS)
c
#include "MQTTClient.h"
#define BROKER_URI "ssl://broker.example.com:8883"
#define DEVICE_ID "device-001"
#define KEEPALIVE_SEC 60
static MQTTClient s_client;
static MQTTClient_connectOptions s_conn_opts = MQTTClient_connectOptions_initializer;
int mqtt_message_arrived(void *ctx, char *topic, int topic_len,
MQTTClient_message *msg)
{
/* Process command from broker */
handle_command(topic, msg->payload, msg->payloadlen);
MQTTClient_freeMessage(&msg);
MQTTClient_free(topic);
return 1; /* 1 = message handled; 0 asks the library to deliver it again */
}
int mqtt_init(void)
{
MQTTClient_create(&s_client, BROKER_URI, DEVICE_ID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
s_conn_opts.keepAliveInterval = KEEPALIVE_SEC;
s_conn_opts.cleansession = 1;
s_conn_opts.username = "devices";
s_conn_opts.password = "secret";
MQTTClient_SSLOptions ssl = MQTTClient_SSLOptions_initializer;
ssl.trustStore = "/etc/ssl/mqtt-ca.pem";
s_conn_opts.ssl = &ssl;
MQTTClient_setCallbacks(s_client, NULL, NULL, mqtt_message_arrived, NULL);
int rc = MQTTClient_connect(s_client, &s_conn_opts);
if (rc != MQTTCLIENT_SUCCESS) { return rc; }
MQTTClient_subscribe(s_client, "device/" DEVICE_ID "/cmd", 1);
return 0;
}
BLE Advertising (nRF52840, nRF Connect SDK)
c
#include <bluetooth/bluetooth.h>
/* Connectable advertising with device name + service UUID in AD */
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL,
BT_UUID_16_ENCODE(0x181A)), /* Environmental Sensing */
};
static const struct bt_data sd[] = {
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME,
sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};
void ble_start_advertising(void)
{
bt_enable(NULL);
bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
}

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/ormus-solutions/libreembed-claude-code/iot-protocols/badge)](https://verified-skill.com/skills/ormus-solutions/libreembed-claude-code/iot-protocols)

Something wrong with this skill or its page? Tell us.