Skip to content

Prove the LAN loop

Two LAN behaviours, both required before Act 2:

  1. Closed loop — BOOT on a zone → gg-edge/sensor → GgEdgeLoop → gg-edge/actuator/<thing> → that zone’s RGB green on press, off on release. Other zones stay dark.
  2. Telemetry — each board publishes chip temperature every 10 s on gg-edge/telemetry/<thing>. The local loop does not subscribe to it; Act 2 archives, alarms, and trains on it.
ESP32-S3BOOT · chip temp
→
NUC coreMoquetteGgEdgeLoop
→
ESP32-S3RGB green / off
Optional mirror: bridge → IoT Core on sensor / actuator (telemetry bridged in Act 2)
Button loop never needs the cloud. Telemetry streams on the LAN now; AWS consumes it in Act 2.
TopicWho publishesWho consumes on the LAN
gg-edge/sensorESP32-S3 (BOOT press / release)

com.example.GgEdgeLoop (via bridge LocalMqtt → Pubsub)

gg-edge/actuator/<thing>GgEdgeLoopThat zone’s firmware (RGB)
gg-edge/telemetry/<thing>ESP32-S3 (every 10 s)

Nobody on the core yet — prove it on serial; Act 2 adds consumers

RGB colors in this lab: green = button loop (this page), blue = cloud command (Act 2), red = edge model (Act 2). GgEdgeLoop 1.0.1 does not send a color field; the firmware defaults to green.

Use an ESP32-S3
Espressif ESP32-S3 — Wi‑Fi microcontroller used here as the Greengrass client device (not a second core).
DevKitC-1 (or equivalent).

3V33V3RST4567151617188346910111213145VGGTXRX1242414039383736350454847212019GGESP32-S3-WROOM-1ESPRESSIF1117CRGBgreen = onRSTBOOTGPIO 0UARTUSB-UARTUSB
  1. BOOTRight side, below RST — press for senseGPIO 0 → gg-edge/sensor
  2. RGB (actuator)White square, lower-left. Turns green on press, off on release. GPIO 48 (v1.0 default) or 38 (v1.1).WS2812 → gg-edge/actuator
  3. USBBottom right USB-C — flash / serial (USB Serial/JTAG)
DevKit-class ESP32-S3-WROOM-1 (USB-C). Layout matches common boards and Espressif’s DevKitC-1 guide. Lab: BOOT + onboard RGB.
  1. Nucleus
    Greengrass Nucleus — the edge runtime on the core device. Installs components, talks to AWS, and hosts the local client-device stack.
    healthy; Auth
    aws.greengrass.clientdevices.Auth — Greengrass component that decides which client device certificates may connect to the local MQTT broker.
    , Moquette
    aws.greengrass.clientdevices.mqtt.Moquette — local MQTT broker on the core. Not Mosquitto; client devices connect here over mTLS.
    , Bridge, IPDetector
    aws.greengrass.clientdevices.IPDetector — publishes the core's LAN address so discovery returns a reachable host for Moquette.
    , Cli, com.example.GgEdgeLoop RUNNING (1.0.1).
  2. Each zone’s serial shows discovery → mqtt connected to core → subscribe to its actuator topic → a telemetry JSON line every ~10 s.
  3. Press BOOT on one board.
  4. NUC loop log shows sensor + actuator for that thing only.
  5. That board’s RGB turns green on press, off on release. The other zone stays dark.
  6. Optional: IoT Core
    AWS IoT Core — cloud MQTT and device identity service. Client devices use it for discovery; the MQTT bridge forwards zone topics here, where IoT rules route them to other AWS services.
    MQTT test client on gg-edge/sensor and gg-edge/actuator/+ (current bridge maps those). Telemetry is not bridged to IoT Core until Deploy from the cloud.
  7. Optional second board: press its BOOT — only that board’s RGB responds.
Terminal window
aws greengrassv2 list-core-devices --status HEALTHY \
--query "coreDevices[?coreDeviceThingName=='${CORE_THING_NAME}'].[coreDeviceThingName,status]" \
--output text
gg-edge-wt-dev-core HEALTHY
Terminal window
sudo /greengrass/v2/bin/greengrass-cli component list | grep -A2 GgEdgeLoop
Component Name: com.example.GgEdgeLoop
Version: 1.0.1
State: RUNNING

idf.py -p /dev/ttyACM0 monitor (or your port). Skip pasting the discovery JSON’s CA PEM into tickets — the host:port line is enough. Example logs use 192.0.2.10 for the NUC; your discovery address will be the core’s real LAN IP.

I (257) gg-edge: RGB strip on GPIO 48 — boot blink
I (1007) gg-edge: RGB -> off
I (2217) gg-edge: chip temperature sensor ready: 28.9 C
I (3017) gg-edge: wifi connected
I (4197) gg-edge: discovered core 192.0.2.10:8883
I (4197) gg-edge: mqtt connected to core
I (4197) gg-edge: subscribed gg-edge/actuator/gg-edge-wt-dev-esp32-1
I (13207) gg-edge: telemetry {"thing":"gg-edge-wt-dev-esp32-1","tempC":28.90,"rssi":-29,"uptimeS":13,"rgb":"off"} msg_id=0
I (23207) gg-edge: telemetry {"thing":"gg-edge-wt-dev-esp32-1","tempC":28.90,"rssi":-29,"uptimeS":23,"rgb":"off"} msg_id=0

Pass criteria:

  • chip temperature sensor ready once at boot
  • a telemetry JSON line about every 10 s
  • thing matches the board’s Thing name
  • tempC is a few degrees above room temperature (die sensor, coarse steps)
  • rgb is "off" when the LED is clear

Repeat on the second zone if you have one — expect a different baseline tempC (for example ~28.9 °C vs ~26.5 °C on the bench).

Terminal window
sudo tail -f /greengrass/v2/logs/com.example.GgEdgeLoop.log

Press and release BOOT on esp32-1:

2026-10-09T19:50:36.780Z [INFO] (Copier) com.example.GgEdgeLoop: stdout. INFO:GgEdgeLoop:sensor event: {"event":"press","thing":"gg-edge-wt-dev-esp32-1"}. {scriptName=services.com.example.GgEdgeLoop.lifecycle.Run, serviceName=com.example.GgEdgeLoop, currentState=RUNNING}
2026-10-09T19:50:36.785Z [INFO] (Copier) com.example.GgEdgeLoop: stdout. INFO:GgEdgeLoop:published actuator command on gg-edge/actuator/gg-edge-wt-dev-esp32-1: {'state': 'on', 'source': 'com.example.GgEdgeLoop', 'target': 'gg-edge-wt-dev-esp32-1', 'echo': {'event': 'press', 'thing': 'gg-edge-wt-dev-esp32-1'}}. {scriptName=services.com.example.GgEdgeLoop.lifecycle.Run, serviceName=com.example.GgEdgeLoop, currentState=RUNNING}
2026-10-09T19:50:36.928Z [INFO] (Copier) com.example.GgEdgeLoop: stdout. INFO:GgEdgeLoop:sensor event: {"event":"release","thing":"gg-edge-wt-dev-esp32-1"}. {scriptName=services.com.example.GgEdgeLoop.lifecycle.Run, serviceName=com.example.GgEdgeLoop, currentState=RUNNING}
2026-10-09T19:50:36.931Z [INFO] (Copier) com.example.GgEdgeLoop: stdout. INFO:GgEdgeLoop:published actuator command on gg-edge/actuator/gg-edge-wt-dev-esp32-1: {'state': 'off', 'source': 'com.example.GgEdgeLoop', 'target': 'gg-edge-wt-dev-esp32-1', 'echo': {'event': 'release', 'thing': 'gg-edge-wt-dev-esp32-1'}}. {scriptName=services.com.example.GgEdgeLoop.lifecycle.Run, serviceName=com.example.GgEdgeLoop, currentState=RUNNING}

Pass criteria in those lines:

  • thing / target match the board you pressed
  • topic is gg-edge/actuator/<that-thing> (not a bare gg-edge/actuator)
  • state is on for press and off for release
  • no color field yet (firmware still paints green)
I (45102) gg-edge: published sensor event=press msg_id=12
I (45118) gg-edge: actuator msg on gg-edge/actuator/gg-edge-wt-dev-esp32-1: {"state": "on", "source": "com.example.GgEdgeLoop", "target": "gg-edge-wt-dev-esp32-1", "echo": {"event": "press", "thing": "gg-edge-wt-dev-esp32-1"}}
I (45120) gg-edge: RGB -> green (on)
I (45340) gg-edge: published sensor event=release msg_id=13
I (45355) gg-edge: actuator msg on gg-edge/actuator/gg-edge-wt-dev-esp32-1: {"state": "off", "source": "com.example.GgEdgeLoop", "target": "gg-edge-wt-dev-esp32-1", "echo": {"event": "release", "thing": "gg-edge-wt-dev-esp32-1"}}
I (45356) gg-edge: RGB -> off

The next telemetry line should show "rgb":"off" again after release. While you hold BOOT it may show "rgb":"green".

Do not paste device certs or private keys into tickets or docs.

Press BOOT on gg-edge-wt-dev-esp32-2. NUC log should show that thing only:

INFO:GgEdgeLoop:sensor event: {"event":"press","thing":"gg-edge-wt-dev-esp32-2"}
INFO:GgEdgeLoop:published actuator command on gg-edge/actuator/gg-edge-wt-dev-esp32-2: {'state': 'on', 'source': 'com.example.GgEdgeLoop', 'target': 'gg-edge-wt-dev-esp32-2', 'echo': {'event': 'press', 'thing': 'gg-edge-wt-dev-esp32-2'}}

esp32-1 serial must not show an actuator message for that press.

CheckPass?

list-core-devices shows HEALTHY

GgEdgeLoop 1.0.1 RUNNING

Discovery JSON lists core host:8883

Serial: mqtt connected to core → subscribed gg-edge/actuator/<thing>

Serial: chip temperature sensor ready and telemetry JSON every ~10 s

BOOT press publishes sensor event for that thing

Component log publishes to gg-edge/actuator/<thing>

Onboard RGB green on press, off on release (that board only)

Other zone RGB stays off for that press

IoT Core sees gg-edge/sensor / gg-edge/actuator/+ (optional)

So far the cloud may mirror sensor/actuator, but nothing consumes telemetry. Act 2 · Wire into AWS starts next: deploy the stack from S3 (adds telemetry bridge routes + LogManager), archive readings, keep zone state, raise an alarm, drive the RGB blue from a Lambda, and run a SageMaker-trained model on the core (red RGB).

Next: Architecture.