From d23126451d7fb11aec8d84e36be1adbbdae0bc05 Mon Sep 17 00:00:00 2001 From: Kristjan Komlosi Date: Wed, 11 Feb 2026 13:00:25 +0100 Subject: [PATCH] move Arduino sketch to separate git repo --- esp-demo.ino | 308 +++++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 3 + 2 files changed, 311 insertions(+) create mode 100644 esp-demo.ino create mode 100644 readme.md diff --git a/esp-demo.ino b/esp-demo.ino new file mode 100644 index 0000000..e885e8d --- /dev/null +++ b/esp-demo.ino @@ -0,0 +1,308 @@ +#include +#include +#include +#include +#include + + +const char *WIFI_SSID = "seminardemo"; +const char *WIFI_PASS = "seminardemo"; + +const char *MQTT_HOST = "192.168.1.16"; // broker +const uint16_t MQTT_PORT = 1883; +const char *MQTT_USER = "testuser"; // from test/.env +const char *MQTT_PASS = "testpass"; // from test/.env + +const char *MQTT_TOPIC = "lambdaiot"; // main topic + +// GPIO +const uint8_t ACTOR_PIN = 14; // digital output + +// Publishing interval (ms) +const unsigned long SENSOR_PUBLISH_INTERVAL = 5000; + +WiFiClient wifiClient; +PubSubClient mqtt(wifiClient); + +String macAddress; +String deviceId; +String sensorId; +String actorId; + +unsigned long lastPublishAt = 0; + +// DNS namespace UUID bytes: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 +const char *DNS_NAMESPACE_UUID = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"; + +// ---- UUID helpers ---- + +bool parseUuidBytes(const String &uuidStr, uint8_t out[16]) { + String hex = uuidStr; + hex.replace("-", ""); + if (hex.length() != 32) { + return false; + } + for (int i = 0; i < 16; i++) { + char c1 = hex[i * 2]; + char c2 = hex[i * 2 + 1]; + auto hexVal = [](char c) -> int { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; + }; + int v1 = hexVal(c1); + int v2 = hexVal(c2); + if (v1 < 0 || v2 < 0) { + return false; + } + out[i] = (uint8_t)((v1 << 4) | v2); + } + return true; +} + +String formatUuid(const uint8_t bytes[16]) { + char buf[37]; + snprintf(buf, sizeof(buf), + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], + bytes[6], bytes[7], + bytes[8], bytes[9], + bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); + return String(buf); +} + +bool parseHexBytes(const String &hex, uint8_t *out, size_t outLen) { + if (hex.length() < (int)(outLen * 2)) { + return false; + } + for (size_t i = 0; i < outLen; i++) { + char c1 = hex[i * 2]; + char c2 = hex[i * 2 + 1]; + auto hexVal = [](char c) -> int { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; + }; + int v1 = hexVal(c1); + int v2 = hexVal(c2); + if (v1 < 0 || v2 < 0) { + return false; + } + out[i] = (uint8_t)((v1 << 4) | v2); + } + return true; +} + +String uuidV5(const uint8_t namespaceBytes[16], const String &name) { + // SHA1(namespace + name) + size_t totalLen = 16 + name.length(); + uint8_t *buf = (uint8_t *)malloc(totalLen); + if (!buf) { + return String(""); + } + memcpy(buf, namespaceBytes, 16); + memcpy(buf + 16, name.c_str(), name.length()); + + String hashHex = sha1(buf, totalLen); + free(buf); + + uint8_t out[16]; + if (!parseHexBytes(hashHex, out, sizeof(out))) { + return String(""); + } + + // Set version (5) and variant (RFC 4122) + out[6] = (out[6] & 0x0F) | 0x50; + out[8] = (out[8] & 0x3F) | 0x80; + + return formatUuid(out); +} + +// ---- MQTT messaging ---- + +void publishDiscovery() { + StaticJsonDocument<1024> doc; + doc["mac_address"] = macAddress; + + JsonObject dev = doc.createNestedObject("device"); + dev["id"] = deviceId; + dev["name"] = "ESP8266 Demo"; + dev["description"] = "ESP8266 MQTT demo device"; + dev["location"] = "Lab"; + dev["status_id"] = 1; + + JsonArray sensors = doc.createNestedArray("sensors"); + JsonObject s0 = sensors.createNestedObject(); + s0["id"] = sensorId; + s0["name"] = "Temperature"; + s0["type"] = "ADC"; + s0["data_type_id"] = 2; + + JsonArray actors = doc.createNestedArray("actors"); + JsonObject a0 = actors.createNestedObject(); + a0["id"] = actorId; + a0["name"] = "DigitalOut"; + a0["type"] = "GPIO"; + a0["data_type_id"] = 1; + + char buf[1024]; + size_t n = serializeJson(doc, buf, sizeof(buf)); + + Serial.println(n); + Serial.println(buf); + + String topic = String(MQTT_TOPIC) + "/discovery"; + Serial.println(topic); + int result = mqtt.publish(topic.c_str(), buf, n); + Serial.println(result); +} + +void publishSensorReading() { + int adc = analogRead(A0); + float value = (float)adc; // raw ADC value + + StaticJsonDocument<256> doc; + doc["type"] = "sensor_reading"; + doc["sensor_id"] = sensorId; + doc["value"] = value; + + char buf[256]; + size_t n = serializeJson(doc, buf, sizeof(buf)); + mqtt.publish(MQTT_TOPIC, buf, n); +} + +void publishDeviceCheckResponse() { + StaticJsonDocument<256> doc; + doc["type"] = "device_check_response"; + doc["device_id"] = deviceId; + doc["status"] = "ok"; + + char buf[256]; + size_t n = serializeJson(doc, buf, sizeof(buf)); + mqtt.publish(MQTT_TOPIC, buf, n); +} + +void handleActorCommand(JsonObject payload) { + if (!payload.containsKey("actor_id")) return; + String id = payload["actor_id"].as(); + if (id != actorId) return; + + int value = 0; + if (payload.containsKey("value")) { + if (payload["value"].is()) { + value = payload["value"].as() ? 1 : 0; + } else if (payload["value"].is()) { + value = payload["value"].as() != 0 ? 1 : 0; + } else if (payload["value"].is()) { + value = payload["value"].as() != 0 ? 1 : 0; + } else if (payload["value"].is()) { + String v = payload["value"].as(); + v.toLowerCase(); + if (v == "true" || v == "1" || v == "on") value = 1; + else value = 0; + } + } + + digitalWrite(ACTOR_PIN, value ? HIGH : LOW); +} + +void handleSensorTrigger(JsonObject payload) { + if (!payload.containsKey("sensor_id")) return; + String id = payload["sensor_id"].as(); + if (id != sensorId) return; + publishSensorReading(); +} + +void handleDeviceCheckRequest(JsonObject payload) { + if (!payload.containsKey("device_id")) return; + String id = payload["device_id"].as(); + if (id != deviceId) return; + publishDeviceCheckResponse(); +} + +void mqttCallback(char *topic, uint8_t *payload, unsigned int length) { + StaticJsonDocument<512> doc; + DeserializationError err = deserializeJson(doc, payload, length); + if (err) { + return; + } + + const char *type = doc["type"] | ""; + String msgType = String(type); + msgType.toLowerCase(); + + if (msgType == "actor_command") { + handleActorCommand(doc.as()); + return; + } + if (msgType == "sensor_trigger") { + handleSensorTrigger(doc.as()); + return; + } + if (msgType == "device_check_request") { + handleDeviceCheckRequest(doc.as()); + return; + } +} + +// ---- Setup/loop ---- + +void ensureMqtt() { + while (!mqtt.connected()) { + String clientId = "esp-demo-" + String(ESP.getChipId()); + if (mqtt.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) { + mqtt.subscribe(MQTT_TOPIC); + delay(1000); + publishDiscovery(); + } else { + delay(2000); + } + } +} + +void setup() { + pinMode(ACTOR_PIN, OUTPUT); + digitalWrite(ACTOR_PIN, LOW); + Serial.begin(9600); + + WiFi.mode(WIFI_STA); + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + } + + macAddress = WiFi.macAddress(); + macAddress.toLowerCase(); + + uint8_t dnsNs[16]; + parseUuidBytes(String(DNS_NAMESPACE_UUID), dnsNs); + String macNoSep = macAddress; + macNoSep.replace(":", ""); + deviceId = uuidV5(dnsNs, macNoSep); + + uint8_t deviceNs[16]; + parseUuidBytes(deviceId, deviceNs); + sensorId = uuidV5(deviceNs, "sensor-0"); + actorId = uuidV5(deviceNs, "actor-0"); + + mqtt.setBufferSize(4096); // Otherwise discovery fails spectacularly + mqtt.setServer(MQTT_HOST, MQTT_PORT); + mqtt.setCallback(mqttCallback); + + ensureMqtt(); + lastPublishAt = millis(); +} + +void loop() { + ensureMqtt(); + mqtt.loop(); + + unsigned long now = millis(); + if (now - lastPublishAt >= SENSOR_PUBLISH_INTERVAL) { + lastPublishAt = now; + publishSensorReading(); + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..720832b --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +# Lambda IoT ESP8266 demo +This demo interacts with the the LambdaIoT Core via an MQTT broker. +Upload it via Arduino IDE