EdgeX Integration

Connect EdgeX Foundry — open-source or EdgeXpert — to AnyLog as a southbound data source, over MQTT or REST.


EdgeX Foundry is an open source, vendor-neutral edge computing framework under the LF Edge umbrella. It provides a southbound platform for connecting IoT devices using standard protocols including Modbus, MQTT, BACnet, SNMP, OPC-UA, REST, and more. EdgeXpert, from IoTech Systems, is a commercial edition of EdgeX with an additional browser-based management GUI for configuring application services — see Option C below if that’s what you’re running.

Integration between EdgeX (either edition) and AnyLog is achieved by configuring EdgeX to export sensor data — over MQTT or REST — to an AnyLog node, which ingests it like any other southbound source.

EdgeX version note: This guide’s Options A and B are written for open-source EdgeX 3.x/4.x (Napa / Odesa). EdgeX 4.0 uses MQTT as the default internal message bus and PostgreSQL as its default database. If you are running an older open-source release, API ports and endpoint paths will differ — see the legacy appendix at the bottom of this page if you’re on something closer to EdgeX 1.x.


Integration options

Option A — AnyLog as the MQTT broker (direct)

Configure EdgeX to publish directly to an AnyLog node running the message broker service. No third-party broker needed. The AnyLog node receiving data can be an Operator (stores data locally) or a Publisher (routes data to Operators).

EdgeX  →  AnyLog Message Broker  →  AnyLog Operator

Option B — Third-party broker

Configure EdgeX to publish to an external MQTT broker (e.g. Eclipse Mosquitto, HiveMQ), and configure AnyLog to subscribe to that broker.

EdgeX  →  MQTT Broker (Mosquitto / HiveMQ)  →  AnyLog msg client

Option C — EdgeXpert (GUI-based)

If you’re running EdgeXpert rather than open-source EdgeX, application services are configured through IoTech’s browser GUI instead of Docker Compose environment variables. Three transfer methods are available from the GUI:

  • REST PUT (no transform)
  • REST POST (with transform)
  • Direct MQTT (with transform)

See Option C below for the full walkthrough.


Prerequisites

  • Open-source EdgeX (Options A/B): deployed via Docker (see EdgeX Quick Start)
  • EdgeXpert (Option C): EdgeX plus the EdgeXpert Management tool installed (see IoTech System / User Guide)
  • An AnyLog node with TCP, REST, Streamer, and Operator services running — see Background Services
  • For Option A: the AnyLog Message Broker service running on the receiving node
  • For Option C’s REST paths: the AnyLog REST service configured — see Using REST
  • For Option C’s Message Broker path: the AnyLog Message Broker service — see Message Broker

Option A — AnyLog as broker

1. Start the AnyLog message broker

On the AnyLog node that will receive EdgeX data:

<run message broker where
    external_ip = !external_ip and external_port = !anylog_broker_port and
    internal_ip = !ip and internal_port = !anylog_broker_port and
    bind = false and threads = 6>

Verify it is running:

get processes       # MSG Broker row should show Running
get local broker

2. Subscribe to the EdgeX topic

Map the incoming EdgeX event structure to an AnyLog database table. The broker=local keyword tells AnyLog to subscribe to its own broker:

<run msg client where
    broker = local and
    log = false and
    topic = (
        name = anylogEdgeX and
        dbms = edgex and
        table = "bring [device]" and
        column.timestamp.timestamp = now and
        column.value.float = "bring [readings][][value]" and
        column.name.str = "bring [readings][][name]"
    )>

Verify the subscription:

get msg clients

3. Configure EdgeX to publish to AnyLog

In your EdgeX deployment, add an application service (app-service-configurable) configured to export events to your AnyLog broker address. In your docker-compose.override.yml:

app-mqtt-export:
  container_name: edgex-app-mqtt-export
  environment:
    EDGEX_PROFILE: mqtt-export
    EDGEX_SECURITY_SECRET_STORE: "false"
    SERVICE_HOST: edgex-app-mqtt-export
    WRITABLE_PIPELINE_FUNCTIONS_MQTTEXPORT_PARAMETERS_BROKERADDRESS: tcp://[anylog-node-ip]:[broker-port]
    WRITABLE_PIPELINE_FUNCTIONS_MQTTEXPORT_PARAMETERS_TOPIC: anylogEdgeX
    WRITABLE_PIPELINE_FUNCTIONS_MQTTEXPORT_PARAMETERS_CLIENTID: edgex-anylog
  image: nexus3.edgexfoundry.org:10004/app-service-configurable:latest
  networks:
    edgex-network:

Replace [anylog-node-ip] and [broker-port] with your AnyLog node’s IP and broker port (default 32550).


Option B — Third-party broker

1. Configure EdgeX to publish to the broker

Add an MQTT export application service to your EdgeX compose file pointing to your third-party broker:

app-mqtt-export:
  container_name: edgex-app-mqtt-export
  environment:
    EDGEX_PROFILE: mqtt-export
    EDGEX_SECURITY_SECRET_STORE: "false"
    SERVICE_HOST: edgex-app-mqtt-export
    WRITABLE_PIPELINE_FUNCTIONS_MQTTEXPORT_PARAMETERS_BROKERADDRESS: tcp://[broker-ip]:[broker-port]
    WRITABLE_PIPELINE_FUNCTIONS_MQTTEXPORT_PARAMETERS_TOPIC: edgex-events
    WRITABLE_PIPELINE_FUNCTIONS_MQTTEXPORT_PARAMETERS_CLIENTID: edgex-export
  image: nexus3.edgexfoundry.org:10004/app-service-configurable:latest
  networks:
    edgex-network:

2. Subscribe AnyLog to the broker

<run msg client where
    broker = [broker-ip] and
    port = [broker-port] and
    user = [user] and
    password = [password] and
    log = false and
    topic = (
        name = edgex-events and
        dbms = edgex and
        table = "bring [device]" and
        column.timestamp.timestamp = now and
        column.value.float = "bring [readings][][value]" and
        column.name.str = "bring [readings][][name]"
    )>

Option C — EdgeXpert (GUI-based)

This option applies to EdgeXpert, IoTech’s commercial edition of EdgeX with a browser management GUI. The example data source below (retail-1) is a sample provided by IoTech.

{
  "apiVersion":"v2",
  "id":"ff3013c3-2f96-48b6-ac06-5eeb4cde1ecf",
  "deviceName":"retail-device-1",
  "profileName":"RetailVirtualDevice",
  "sourceName":"FreezerTemp1",
  "origin":1694737678685722461,
  "readings":[{
    "id":"000a2227-0ff8-4534-8131-310a7efcdd47",
    "origin":1694737678685722461,
    "deviceName":"retail-device-1",
    "resourceName":"FreezerTemp1",
    "profileName":"RetailVirtualDevice",
    "valueType":"Float32",
    "units":"F",
    "value":"3.140839e+01"
  }]
}
{
  "apiVersion":"v2",
  "id":"e60989ad-ab59-456e-bb00-b90518ca83e1",
  "deviceName":"retail-device-1",
  "profileName":"RetailVirtualDevice",
  "sourceName":"PeopleCount",
  "origin":1694737678704913648,
  "readings":[{
    "id":"e789d093-0392-4a4e-9890-eee615f647f2",
    "origin":1694737678704913648,
    "deviceName":"retail-device-1",
    "resourceName":"PeopleCount",
    "profileName":"RetailVirtualDevice",
    "valueType":"Int32",
    "value":"48"
  }]
}

Note: this payload shape (apiVersion, sourceName, resourceName) is EdgeXpert’s v2 API and differs from the open-source EdgeX event shape shown under Data mapping notes below (device, readings[].name). Map against whichever shape your actual deployment sends — don’t assume the two are interchangeable.

Logging into EdgeXpert

  • URL: https://${YOUR_IP}:9090
  • Username: admin Password: admin

Login Screen

From the left-side menu, go to App Services:

Xpert Management Homepage

…then add a Basic Service from the right-side panel. The three transfer methods below all start from this same Basic Service creation screen — what differs is how you configure it from there.

Basic Service location

Transferring data from EdgeX to AnyLog

EdgeXpert can transfer data into AnyLog using three methods:

  • REST PUT — receives data without transformation; the database/table are fixed in the REST headers.
  • REST POST — receives data with transformation; database/table can come from headers or be derived from the data itself via mapping rules.
  • AnyLog Message Broker — transformation supported, delivered over MQTT instead of REST.

AnyLog can also be configured to receive data from third-party brokers (CloudMQTT, Eclipse Mosquitto, etc.) — the same pattern as Option B above.

Method 1: REST PUT (no transformation)

Configuration is done entirely on the EdgeXpert side; AnyLog just needs its REST service running with no mapping rules. The database and table are specified in the REST headers, not derived from the data.

On your own machine, create a JavaScript transform that extracts the reading value EdgeXpert will send — a sample script is available:

// file name: edgex_transformation.js
var outputObject = { value: inputObject.readings[0] };
return outputObject;

In the EdgeXpert GUI, configure the Basic App Service:

  • Basic Info — Name; Destination: HTTP Basic Information
  • Address Info — Method: PUT; URL (your AnyLog node’s REST IP:port); headers: type: json, dbms: <database>, table: <table>, mode: streaming, Content-Type: text/plain Address Information
  • Data Format — JavaScript Transform: edgex_transformation.js Data Format
  • Filter — Device Filter, as needed Filter Value(s)

Save the service (Save Button); data should begin flowing into AnyLog via PUT.

Method 2: REST POST (with transformation)

Here the transformation happens on the AnyLog side via mapping rules attached to a topic, and the database/table can be derived from the incoming data itself rather than fixed in the headers. This uses the same underlying mechanism as running AnyLog as a message broker — mapping rules declared via a client service.

On the AnyLog operator node receiving the data, declare the mapping rules and start the client service:

<run mqtt client where broker=rest and user-agent=anylog and log=false and topic=(
  name=anylogedgex-post and
  dbms=!company_name.name and
  table="bring [readings][0][resourceName]" and
  column.timestamp.timestamp=now and
  column.value=(type=float and value="bring [readings][0][value]"))>

Command name and syntax flagged, not corrected: this example uses run mqtt client and the column.x=(type=... and value=...) mapping form, both as given in the original source. The rest of this page (Options A/B) uses run msg client and column.x.type = "bring [...]" instead. These may be equivalent aliases/forms, or one may be outdated — confirm against the actual command reference before relying on this exact syntax.

In the EdgeXpert GUI, configure the Basic App Service:

  • Basic Info — Name; Destination: HTTP Basic Information
  • Address Info — Method: POST; URL (your AnyLog node’s REST IP:port); headers: command: data, topic: anylogedgex-post, User-Agent: AnyLog/1.23, Content-Type: text/plain Address Information
  • Filter — Device Filter, as needed

Save the service; data should begin flowing into AnyLog via POST.

Method 3: AnyLog Message Broker (with transformation)

Same mapping-rule mechanism as Method 2, but delivered over MQTT to AnyLog’s own broker rather than over REST. The mapping rules must include the database and table, since there are no REST headers to supply them.

<run mqtt client where broker=local and log=false and topic=(
  name=anylogedgex-mqtt and
  dbms=!default_dbms and
  table="bring [readings][0][resourceName]" and
  column.timestamp.timestamp=now and
  column.value=(type=float and value="bring [readings][0][value]"))>

(The same command-name/syntax caveat from Method 2 applies here.)

In the EdgeXpert GUI, configure the Basic App Service:

  • Basic Info — Name; Destination: MQTT Basic Information
  • Address Info — URL (your AnyLog node’s Message Broker IP:port); Topic Address Information
  • Filter — Device Filter, as needed

Save the service; data should begin flowing into AnyLog via the message broker.


Verifying data flow

Check EdgeX is running and producing readings

# List devices (EdgeX v2/v3/v4 API)
curl http://localhost:59881/api/v3/device/all | jq

# View recent readings
curl http://localhost:59880/api/v3/reading/all?limit=10 | jq

Check AnyLog is receiving data

get msg clients                    # subscription status
get streaming                      # buffer status
get operator                       # ingestion status
get rows count where dbms = edgex  # confirm rows are landing

Query the data:

run client () sql edgex format=table "select * from rand_data limit 10"

Data mapping notes

Open-source EdgeX (Options A/B) publishes events in this structure:

{
  "device": "my-sensor",
  "origin": 1700000000,
  "readings": [
    {
      "name": "temperature",
      "value": "23.5",
      "origin": 1700000000
    }
  ]
}

The bring expressions in the run msg client command extract values from this structure:

AnyLog column Mapping Notes
table bring [device] Uses the device name as the table name
timestamp now Uses AnyLog ingestion time
value bring [readings][][value] First reading value
name bring [readings][][name] First reading name

Adjust the mapping to match the actual structure of your EdgeX device readings. EdgeXpert’s event structure (Option C) uses different field names (deviceName, sourceName, resourceName) — see the payload example under Option C rather than reusing this table for that path.


Deploying EdgeX

For a quick local open-source EdgeX deployment using the EdgeX compose builder:

git clone https://github.com/edgexfoundry/edgex-compose.git
cd edgex-compose/compose-builder

# Generate a compose file with MQTT support and no security (for dev/test)
make gen no-secty mqtt-broker ui

# Start EdgeX
docker compose up -d

# Verify services are running
docker ps

See the EdgeX Quick Start for full deployment instructions. EdgeXpert deployment is a separate, commercial process — see IoTech System / User Guide.


Appendix — Legacy EdgeX 1.3.1 Complete Example

This is a concrete deployment log against EdgeX 1.3.1 — several major versions behind current EdgeX — using the AnyLog-co/lfedge-code demo repo and the deprecated v1 reading API (/api/v1/reading). Treat it as a snapshot of an older release rather than current guidance: Options A/B above cover the same integration goal against current EdgeX using the supported edgex-compose deployment path, and are the ones to follow for a new deployment.

The demo uses EdgeX’s random data generator, with data sent over its app-service-mqtt. Fields that change between deployments are set in bold below.

Steps

  1. Clone lfedge-code:
    git clone https://github.com/AnyLog-co/lfedge-code 
    cd lfedge-code/edgex 
    
  2. Update configurations in the .env file:
    1. Update the MQTT params to match the credentials on your AnyLog operator node (e.g. anylog-operator-node1):
       MQTT_TOPIC=anylogedgex
       MQTT_IP_ADDRESS=10.0.0.50
       MQTT_PORT=32150
       MQTT_USER=""
       MQTT_PASSWORD=""
      
    2. If you’re on ARM64, update the ARCH value on line 27:
       # default amd64 machine 
      ARCH=""
       # update to arm64 machine 
      ARCH=-arm64
      
  3. Start the EdgeX instance:
    cd lfedge-code/edgex 
    docker-compose up -d
    

Validate Deployment

  1. Confirm nothing crashed: docker ps -a | grep edgex
    root@edgex-operator2:~# docker ps -a | grep edgex
    a13b169023b7   emqx/kuiper:1.1.1-alpine                                                   "/usr/bin/docker-ent…"   45 hours ago   Up 44 hours             127.0.0.1:20498->20498/tcp, 9081/tcp, 127.0.0.1:48075->48075/tcp                       edgex-kuiper
    56a3482bdfc8   edgexfoundry/docker-sys-mgmt-agent-go:1.3.1                                "/sys-mgmt-agent -cp…"   45 hours ago   Up 44 hours             127.0.0.1:48090->48090/tcp                                                             edgex-sys-mgmt-agent
    2740875f17a4   edgexfoundry/docker-app-service-configurable:1.3.1                         "/app-service-config…"   45 hours ago   Up 44 hours             48095/tcp, 127.0.0.1:48101->48101/tcp                                                  edgex-app-service-configurable-mqtt
    32b749bbd104   edgexfoundry/docker-device-random-go:1.3.1                                 "/device-random --cp…"   45 hours ago   Up 44 hours             127.0.0.1:49988->49988/tcp                                                             edgex-device-random
    e960f5ff00c5   edgexfoundry/docker-app-service-configurable:1.3.1                         "/app-service-config…"   45 hours ago   Up 44 hours             48095/tcp, 127.0.0.1:48100->48100/tcp                                                  edgex-app-service-configurable-rules
    e50c8de4b879   edgexfoundry/docker-device-modbus-go:1.3.1                                 "/device-modbus --cp…"   45 hours ago   Up 44 hours             127.0.0.1:49991->49991/tcp                                                             edgex-device-modbus
    e91cd4ad63fa   edgexfoundry/docker-core-command-go:1.3.1                                  "/core-command -cp=c…"   45 hours ago   Up 44 hours             127.0.0.1:48082->48082/tcp                                                             edgex-core-command
    59464a3a976d   edgexfoundry/docker-core-data-go:1.3.1                                     "/core-data -cp=cons…"   45 hours ago   Up 44 hours             127.0.0.1:5563->5563/tcp, 127.0.0.1:48080->48080/tcp                                   edgex-core-data
    b706d9584413   edgexfoundry/docker-core-metadata-go:1.3.1                                 "/core-metadata -cp=…"   45 hours ago   Up 44 hours             127.0.0.1:48081->48081/tcp                                                             edgex-core-metadata
    13abb3559d2a   edgexfoundry/docker-support-notifications-go:1.3.1                         "/support-notificati…"   45 hours ago   Up 44 hours             127.0.0.1:48060->48060/tcp                                                             edgex-support-notifications
    ff7d350ca3ba   edgexfoundry/docker-support-scheduler-go:1.3.1                             "/support-scheduler …"   45 hours ago   Up 44 hours             127.0.0.1:48085->48085/tcp                                                             edgex-support-scheduler
    0346e264f6d6   edgexfoundry/docker-edgex-consul:1.3.0                                     "edgex-consul-entryp…"   45 hours ago   Up 44 hours             8300-8302/tcp, 8400/tcp, 8301-8302/udp, 8600/tcp, 8600/udp, 127.0.0.1:8500->8500/tcp   edgex-core-consul
    fab7cbdcc29f   redis:6.0.9-alpine                                                         "docker-entrypoint.s…"   45 hours ago   Up 44 hours             127.0.0.1:6379->6379/tcp                                                               edgex-redis
    f135b724626e   nexus3.edgexfoundry.org:10003/edgex-devops/edgex-modbus-simulator:latest   "/simulator"             45 hours ago   Up 44 hours             127.0.0.1:1502->1502/tcp                                                               edgex-modbus-simulator
    
  2. Confirm data is coming in: curl http://127.0.0.1:48080/api/v1/reading 2> /dev/null
    [
      {
     "id": "000767a3-61bb-49e1-93ff-be4695eb5b43",
     "created": 1659412501505,
     "origin": 1659412501498780400,
     "device": "Random-Integer-Generator01",
     "name": "RandomValue_Int16",
     "value": "10380",
     "valueType": "Int16"
      },
      {
     "id": "000797b4-736b-48b9-bbe5-09594be7099f",
     "created": 1659403221133,
     "origin": 1659403221133020700,
     "device": "Random-Integer-Generator01",
     "name": "RandomValue_Int32",
     "value": "771919435",
     "valueType": "Int32"
      },
      {
     "id": "00079ae9-340a-4d70-9e0b-9a5a8b48e216",
     "created": 1659412541467,
     "origin": 1659412541463869400,
     "device": "Random-Integer-Generator01",
     "name": "RandomValue_Int8",
     "value": "27",
     "valueType": "Int8"
      },
      {
     "id": "0008d23e-7640-4974-aaeb-179e375566cb",
     "created": 1659425642023,
     "origin": 1659425642019472400,
     "device": "Random-Integer-Generator01",
     "name": "RandomValue_Int8",
     "value": "-87",
     "valueType": "Int8"
      },
      {
     "id": "000f547c-25b7-4d96-b862-67467345a74c",
     "created": 1659461623534,
     "origin": 1659461623530927000,
     "device": "Random-Integer-Generator01",
     "name": "RandomValue_Int8",
     "value": "-49",
     "valueType": "Int8"
      },
      ...
    ]
    

Further reading