Bring your Own MQTT Broker (using Mosquitto)

Create the MQTT Bridge

  1. Run the following command in the terminal to create a folder of artifacts and an empty script file.
mkdir -p ~/environment/GreengrassCore/artifacts/com.example.MqttBridge/1.0.0 && cd ~/environment/GreengrassCore/artifacts/com.example.MqttBridge/1.0.0
touch mosquitto.conf bridge.conf cleanup.sh startup.sh
  1. Open the created empty script files and paste the following source code respectively.

mosquitto.conf

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
# =================================================================
# Default listener
# =================================================================

# Port to use for the default listener.
port 1883

# Choose the protocol to use when listening.
# This can be either mqtt or websockets.
# Websockets support is currently disabled by default at compile time.
# Certificate based TLS may be used with websockets, except that
# only the cafile, certfile, keyfile and ciphers options are supported.
protocol mqtt

bridge.conf

Replace xxxxxxxxxxxxxxx-ats.iot.eu-west-1.amazonaws.com with the endpoint address from your account.

Retrieve your endpoint by running aws iot describe-endpoint --endpoint-type iot:Data-ATS --region [Your Region].

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
# ============================================================
# Bridge to AWS IOT
# ============================================================

connection awsiot

#<Paste your AWS IoT Core ATS endpoint retrieved from the AWS CLI in the form of xxxxxxxxxxxxxxx-ats.iot.<region>.amazonaws.com:8883

address xxxxxxxxxxxxxxx-ats.iot.eu-west-1.amazonaws.com:8883

# Specifying which topics are bridged and in what fashion
#topic # in 1
#topic # out 1
topic # both 1

# Setting protocol versaion explicitly
bridge_protocol_version mqttv311
bridge_insecure false

# Bridge connection name and MQTT client Id, enabling the connection automatically when the broker starts.
cleansession true
clientid bridgeawsiot
start_type automatic
notifications false
log_type all

# ============================================================
# Certificate based SSL/TLS support
# ============================================================

#Path to the rootCA
bridge_cafile /etc/mosquitto/certs/rootCA.pem

# Path to the PEM encoded client certificate
bridge_certfile /etc/mosquitto/certs/cert.crt

# Path to the PEM encoded client private key
bridge_keyfile /etc/mosquitto/certs/private.key

#END of bridge.conf

startup.sh

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
#!/bin/bash
DIR=$(dirname $(realpath $0))
echo "DIR=$DIR"
POLICY_NAME=bridgeMQTT
echo 'get-policy'
aws iot get-policy --policy-name $POLICY_NAME > /dev/null 2>&1
rv=$?
echo "get-policy rv=$rv"
if [ $rv -ne 0 ]; then
    echo 'create policy'
    aws iot create-policy --policy-name $POLICY_NAME --policy-document '{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": "iot:*","Resource": "*"}]}'
    sleep 2
fi
echo 'create-keys-and-certificate'
TMP_FILE=$(mktemp)
echo "TMP_FILE=$TMP_FILE"
aws iot create-keys-and-certificate --set-as-active \
    --certificate-pem-outfile /etc/mosquitto/certs/cert.crt \
    --private-key-outfile /etc/mosquitto/certs/private.key \
    --public-key-outfile /etc/mosquitto/certs/public.key \
    --output json > $TMP_FILE
CERTIFICATE_ARN=$(jq -r ".certificateArn" $TMP_FILE)
CERTIFICATE_ID=$(jq -r ".certificateId" $TMP_FILE)
echo "CERTIFICATE_ID=$CERTIFICATE_ID CERTIFICATE_ARN=$CERTIFICATE_ARN"
echo "CERTIFICATE_ID=$CERTIFICATE_ID" > /etc/mosquitto/certs/cert-id.txt
echo 'attach-principal-policy'
aws iot attach-principal-policy --policy-name bridgeMQTT --principal $CERTIFICATE_ARN
chmod 644 /etc/mosquitto/certs/private.key
chmod 644 /etc/mosquitto/certs/cert.crt
rm -f $TMP_FILE
exit 0

cleanup.sh

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
#!/bin/sh

echo 'Cleaning Up'

CERTIFICATE_ID=theCertificate
REGION=theRegion

echo 'Retrieving Certificate Arn'
CERTIFICATE_ARN=$(aws iot describe-certificate --certificate-id $CERTIFICATE_ID --region $REGION| jq '.certificateDescription.certificateArn')
echo 'Detaching policy from certificate'
aws iot detach-principal-policy --principal $CERTIFICATE_ARN --policy-name bridgeMQTT --region $REGION
echo 'Revoking Certificate'
aws iot update-certificate --certificate-id $CERTIFICATE_ID --new-status 'INACTIVE' --region $REGION
sleep 2
echo 'Deleting Mosquitto device Certificate'
aws iot delete-certificate --certificate-id $CERTIFICATE_ID --region $REGION
sleep 2
echo 'Deleting MqttBridge IoT Policy'
aws iot delete-policy --policy-name bridgeMQTT --region $REGION
  1. Run the following command in the terminal to create a recipe folder and an empty file
touch ~/environment/GreengrassCore/recipes/com.example.MqttBridge-1.0.0.json 
  1. Open the created empty file and paste the following contents. Replace [YOUR_REGION] with the region that you are using for the workshop.

Don’t forget to change [YOUR_REGION].

com.example.MqttBridge-1.0.0.json

{
  "RecipeFormatVersion": "2020-01-25",
  "ComponentName": "com.example.MqttBridge",
  "ComponentVersion": "1.0.0",
  "ComponentDescription": "MQTT Bridge implementation using Mosquito Broker",
  "ComponentPublisher": "Amazon",
  "ComponentConfiguration": {
    "DefaultConfiguration": {
      "region": "[YOUR_REGION]"
    }
  },
  "ComponentDependencies": {
    "aws.greengrass.TokenExchangeService": {
      "VersionRequirement": "^2.0.0",
      "DependencyType": "HARD"
    }
  },
  "Manifests": [
    {
      "Platform": {
        "os": "linux"
      },
      "Lifecycle": {
        "Install": {
          "RequiresPrivilege": true,
          "script": "echo 'mosquitto broker'\napt-get install -y mosquitto mosquitto-clients\necho '/etc/mosquitto/certs'\ntest ! -d /etc/mosquitto/certs && mkdir -p /etc/mosquitto/certs\necho 'rootCA'\ntest ! -e /etc/mosquitto/certs/rootCA.pem && wget https://www.amazontrust.com/repository/AmazonRootCA1.pem -O /etc/mosquitto/certs/rootCA.pem\nexit 0"
        },
        "Startup": {
          "RequiresPrivilege": true,
          "script": "export AWS_DEFAULT_REGION={configuration:/region}\nchmod +x {artifacts:path}/startup.sh\n{artifacts:path}/startup.sh\ncat {artifacts:path}/mosquitto.conf {artifacts:path}/bridge.conf > /etc/mosquitto/mosquitto.conf\nservice mosquitto start"
        },
        "Shutdown": {
          "RequiresPrivilege": true,
          "script": "service mosquitto stop\nexport AWS_DEFAULT_REGION={configuration:/region}\nchmod +x {artifacts:path}/cleanup.sh\n{artifacts:path}/cleanup.sh"
        }
      }
    }
  ]
}
  1. Run the following command to deploy the component
sudo /greengrass/v2/bin/greengrass-cli deployment create \
  --recipeDir ~/environment/GreengrassCore/recipes \
  --artifactDir ~/environment/GreengrassCore/artifacts \
  --merge "com.example.MqttBridge=1.0.0"

To demonstrate the MQTT Bridge in action lets create a publisher and subscriber component.

Create the publisher component

Publisher

  1. Run the following command in the terminal to create a folder of artifacts and an empty script file.
mkdir -p ~/environment/GreengrassCore/artifacts/com.example.BridgePublisher/1.0.0 && cd ~/environment/GreengrassCore/artifacts/com.example.BridgePublisher/1.0.0
touch publisher.sh
  1. Open the created empty script file and paste the following source code.

publisher.sh

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
#!/bin/sh

mosquitto_pub -h localhost -p 1883 -q 1 -d -t $1 -i localClientID -m "{\"message\": \""$2" @ "$(date +"%m-%d-%Y-%H-%M-%s")"\"}"
  1. Run the following command in the terminal to create a recipe folder and an empty file
touch ~/environment/GreengrassCore/recipes/com.example.BridgePublisher-1.0.0.json
  1. Open the created empty file and paste the following contents

com.example.BridgePublisher-1.0.0.json

{
  "RecipeFormatVersion": "2020-01-25",
  "ComponentName": "com.example.BridgePublisher",
  "ComponentVersion": "1.0.0",
  "ComponentDescription": "A component that publishes messages.",
  "ComponentPublisher": "Amazon",
  "ComponentConfiguration": {
    "DefaultConfiguration": {
      "topic": "mytopic1/test",
      "message": "Hello"
    }
  },
  "Manifests": [
    {
      "Platform": {
        "os": "linux"
      },
      "Lifecycle": {
        "Install": {
          "RequiresPrivilege": true,
          "script": "chmod +x {artifacts:path}/publisher.sh"
        },
        "Run": {
          "RequiresPrivilege": true,
          "script": "chmod +x {artifacts:path}/publisher.sh\nwhile true; \ndo \n {artifacts:path}/publisher.sh {configuration:/topic} {configuration:/message}\n  sleep 2\ndone"
        }
      }
    }
  ]
}
  1. Run the following command to deploy the component
sudo /greengrass/v2/bin/greengrass-cli deployment create \
  --recipeDir ~/environment/GreengrassCore/recipes \
  --artifactDir ~/environment/GreengrassCore/artifacts \
  --merge "com.example.BridgePublisher=1.0.0"
  1. Navigate to the AWS Console -> IoT Core -> Test -> MQTT test client. Subscibe to mytopic1/test. If messages are being received then the bridge is working correctly for messages published on the local gateway.

Create the subscriber component

Subscriber

  1. Run the following command in the terminal to create a folder of artifacts and an empty script file.
mkdir -p ~/environment/GreengrassCore/artifacts/com.example.BridgeSubscriber/1.0.0 && cd ~/environment/GreengrassCore/artifacts/com.example.BridgeSubscriber/1.0.0
touch subscriber.sh
  1. Open the created empty script file and paste the following source code.

subscriber.sh

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
#!/bin/sh

mosquitto_sub -h localhost -p 1883 -d -v -t $1
  1. Run the following command in the terminal to create a recipe folder and an empty file
touch ~/environment/GreengrassCore/recipes/com.example.BridgeSubscriber-1.0.0.json
  1. Open the created empty file and paste the following contents

com.example.BridgeSubscriber-1.0.0.json

{
  "RecipeFormatVersion": "2020-01-25",
  "ComponentName": "com.example.BridgeSubscriber",
  "ComponentVersion": "1.0.0",
  "ComponentDescription": "A component that subscribes to a topic",
  "ComponentPublisher": "Amazon",
  "ComponentConfiguration": {
    "DefaultConfiguration": {
      "topic": "mytopic2/test"
    }
  },
  "Manifests": [
    {
      "Platform": {
        "os": "linux"
      },
      "Lifecycle": {
        "Install": {
          "RequiresPrivilege": true,
          "script": "chmod +x {artifacts:path}/subscriber.sh"
        },
        "Run": {
          "RequiresPrivilege": true,
          "script": "{artifacts:path}/subscriber.sh {configuration:/topic}"
        }
      }
    }
  ]
}
  1. Run the following command to deploy the component
sudo /greengrass/v2/bin/greengrass-cli deployment create \
  --recipeDir ~/environment/GreengrassCore/recipes \
  --artifactDir ~/environment/GreengrassCore/artifacts \
  --merge "com.example.BridgeSubscriber=1.0.0"
  1. Run the following command on the core device.

sudo tail -f /greengrass/v2/logs/com.example.BridgeSubscriber.log

  1. Navigate to the AWS Console -> IoT Core -> Test -> MQTT test client. Publish to mytopic2/test.

  2. Back on the core device if topic messages are being received you will see an output like the following

Client mosq-dhTRCo2JGKdYoxSEV6 received PUBLISH (d0, q0, r0, m0, 'mytopic2/test', ... (45 bytes))

Having issues? -> More details can be found in official Troubleshooting section here