Building a serverless connected BBQ as SaaS - Part 1
Tags: aws, serverless, iot
Published: 2024-05-29 10:50:35

---
title: 'Building a serverless connected BBQ as SaaS - Part 1'
description: In the world of BBQ, tradition and technology rarely cross paths. But what if I told you that the future of grilling is here, and it’s connected, smart, and runs on the cloud? In this blog series, I will explore how AWS IoT, serverless, and event-driven architecture enables an automated cooking experience. As a tech-savvy griller, I discover how cloud technology can elevate my grilling game to a whole new level.
cover_image: https://jimmydqv.com/assets/img/post-bbq-saas-part-1/cover-image-dev.png
tags: aws, serverless, iot
canonical_url: https://jimmydqv.com/serverless-bbq-saas/index.html
published: true
---

This post is the start of a series of post on how I built a IoT connected BBQ as SaaS. This first post will kick everything off by briefly introducing the used hardware and AWS GreenGrass, which will be used to connect and send data to the cloud. In the coming post the architecture will be introduced followed be deep dive technical posts on each part of the architecture.

In the end I will have created a Serverless connected BBQ smoker as a SaaS solution. There will be deep dives into IoT setup, on-boarding, user management, data and tenant isolation.

## HW Components

First, let's go over the required HW and what I'll be using in this series. I will be using a [Raspberry Pi 3 model B](https://www.raspberrypi.com/products/raspberry-pi-3-model-b/) that will connect to a [Inkbird IBT-6xs](https://inkbird.com/products/bluetooth-bbq-thermometer-ibt-6xs) over bluetooth LE.

However in this first post we'll setup a simulated device running on a small EC2 instance.

## AWS Greengrass Core

AWS IoT Greengrass is an edge runtime and cloud service for building, deploying, and managing device software in an easy way. It support creation of custom software components that can easily be deployed to any device running Greengrass.

## Greengrass Development Kit

To make development a bit easier I will be using [Greengrass Development Kit](https://docs.aws.amazon.com/greengrass/v2/developerguide/greengrass-development-kit-cli.html) to build new versions of the component. GDK however doesn't support AWS Identity Center and CLI profiles, making it impossible to publish new versions using this tool. Instead I will be manually creating new versions.

## Initialize a component

First of I use GDK to initialize a new component. To do this run the [init command](https://docs.aws.amazon.com/greengrass/v2/developerguide/greengrass-development-kit-cli-component.html#greengrass-development-kit-cli-component-init). There is a need to specify the used language and template to base the component of. Available templates can be found in [GitHub](https://github.com/aws-greengrass/aws-greengrass-component-templates/) I will be using Python and base the component of HelloWorld template. I will also supply a name for it, this will create a new folder where all files are stored. Make sure the folder do not exist.

``` bash
gdk component init -l python -t HelloWorld -n HelloBBQ
```

In the `HelloBBQ` folder there is now a configuration file `gdk-config.json` this need to be updated and the PLACEHOLDER values changed, and the component name specified.

``` json
{
  "component": {
    "com.example.hellobbq": {
      "author": "<PLACEHOLDER_NAME>",
      "version": "1.0.0",
      "build": {
        "build_system": "zip",
        "options": {
          "zip_name": ""
        }
      },
      "publish": {
        "bucket": "<PLACEHOLDER_BUCKET>",
        "region": "<PLACEHOLDER_REGION>"
      }
    }
  },
  "gdk_version": "1.3.0"
}
```

## Add device code

Update the main.py file and add code to connect and publish fake temperatures to IoT Core on a topic `e/{THING_NAME}/data`

``` python
import os
import time
import uuid
import random
import json
import awsiot.greengrasscoreipc.clientv2 as clientV2
from awsiot.greengrasscoreipc.clientv2 import GreengrassCoreIPCClientV2
from decimal import Decimal


# MQTT
IPC_CLIENT: GreengrassCoreIPCClientV2 = clientV2.GreengrassCoreIPCClientV2()
THING_NAME = os.getenv("AWS_IOT_THING_NAME")
OPERATIONS_TOPIC = f"c/{THING_NAME}/operation"
DATA_TOPIC = f"e/{THING_NAME}/data"
QOS = "1"
SESSION = str(uuid.uuid4())


def publishToTopic(topic, payload):
    IPC_CLIENT.publish_to_iot_core(topic_name=topic, qos=QOS, payload=payload)


def createRandomDecimal():
    return random.randint(0, 1000) / 10


def generateSimulatedTemperature():
    tempProbe0 = str(round(Decimal(createRandomDecimal()), 2))
    tempProbe1 = str(round(Decimal(createRandomDecimal()), 2))
    tempProbe2 = str(round(Decimal(createRandomDecimal()), 2))
    tempProbe3 = str(round(Decimal(createRandomDecimal()), 2))
    tempProbe4 = str(round(Decimal(createRandomDecimal()), 2))
    tempProbe5 = str(round(Decimal(createRandomDecimal()), 2))

    # Add the temperatures to the array
    temps = [
        tempProbe0,
        tempProbe1,
        tempProbe2,
        tempProbe3,
        tempProbe4,
        tempProbe5,
    ]

    temp_dict = {"session": SESSION, "temperatures": temps}

    return temp_dict


def main():
    # Continually request information from the iBBQ device
    while True:
        try:
            temps = generateSimulatedTemperature()
            publishToTopic(DATA_TOPIC, json.dumps(temps))
            time.sleep(5)
        except Exception as e:
            print(f"ERROR IN WHILE LOOP, TRY AGAIN! {e.message}")


if __name__ == "__main__":
    main()

```

## Create the recipe

In the same folder the GreenGrass recipe file is also created. This need to be updated to match GDK configuration. In the recipe it's possible to variables like `{artifacts:decompressedPath}`, full reference can be [found here](https://docs.aws.amazon.com/greengrass/v2/developerguide/component-recipe-reference.html)

To send data over MQTT to AWS IoT Core we need to add permissions for the mqttproxy and specify which topics it can publish to. W'll only allow the device to publish to topics it "owns" that is include the thing name. For this we can use the variable `{iot:thingName}`

``` yaml
---
RecipeFormatVersion: "2020-01-25"
ComponentName: "com.example.hellobbq"
ComponentVersion: "1.0.0"
ComponentDescription: "Component for sending temperature data to Cloud"
ComponentPublisher: "YOUR-NAME"
ComponentConfiguration:
  DefaultConfiguration:
    Topic: "e/{iot:thingName}/data"
    accessControl:
      aws.greengrass.ipc.mqttproxy:
        com.example.hellobbq:mqttproxy:1:
          policyDescription: Allows access to publish to device topics.
          operations:
            - aws.greengrass#PublishToIoTCore
          resources:
            - "e/{iot:thingName}/data"
Manifests:
  - Platform:
      os: all
    Artifacts:
      - URI: "s3://PLACEHOLDER_BUCKET/com.example.hellobbq/1.0.0/com.example.hellobbq.zip"
        Unarchive: ZIP
    Lifecycle:
      Run: "python3 -u {artifacts:decompressedPath}/com.example.hellobbq/main.py {configuration:/Topic}"
```

## Build component

With the GDK config and recipe update it's time to build the component. That is done with command.

``` bash
gdk component build
```

The build will produce a zip file located in folder `greengrass-build/COMPONENT_NAME/VERSION/`

With build completed copy the zip file to the S3 bucket used in the recipe.

``` bash
aws s3 cp greengrass-build/artifacts/com.example.HelloBBQ/1.0.0/com.example.hellobbq.zip s3://PLACEHOLDER_BUCKET/com.examle.hellobbq/1.0.0/com.example.hellobbq.zip 
```

With the zip file uploaded the component can be created in the AWS Console.

## Create component

To create the component we navigate to the IoT Core part of the console, expand `Greengrass devices` and select `Components`, click on `Create Component` button to the right.

Paste the recipe and click on `Create Component`

![Image showing create component](https://jimmydqv.com/assets/img/post-bbq-saas-part-1/create-component.png)

## Create simulation device

To simulate the device we can use a small EC2 instance. I will create a small `t3.micro` instance that I will be using.

We need create an IAM Role, that we assign to the EC2 Instance, this role will be used to provision the instance as an IoT Thing and all the needed resources. The minimum access needed can be found [in the aws documentation](https://docs.aws.amazon.com/greengrass/v2/developerguide/provision-minimal-iam-policy.html) below is an example.

``` json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "CreateTokenExchangeRole",
            "Effect": "Allow",
            "Action": [
                "iam:AttachRolePolicy",
                "iam:CreatePolicy",
                "iam:CreateRole",
                "iam:GetPolicy",
                "iam:GetRole",
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::ACCOUNT-ID:role/GreengrassV2TokenExchangeRole",
                "arn:aws:iam::ACCOUNT-ID:policy/GreengrassV2TokenExchangeRoleAccess",
                "arn:aws:iam::aws:policy/GreengrassV2TokenExchangeRoleAccess"
            ]
        },
        {
            "Sid": "CreateIoTResources",
            "Effect": "Allow",
            "Action": [
                "iot:AddThingToThingGroup",
                "iot:AttachPolicy",
                "iot:AttachThingPrincipal",
                "iot:CreateKeysAndCertificate",
                "iot:CreatePolicy",
                "iot:CreateRoleAlias",
                "iot:CreateThing",
                "iot:CreateThingGroup",
                "iot:DescribeEndpoint",
                "iot:DescribeRoleAlias",
                "iot:DescribeThingGroup",
                "iot:GetPolicy"
            ],
            "Resource": "*"
        },
        {
            "Sid": "DeployDevTools",
            "Effect": "Allow",
            "Action": [
                "greengrass:CreateDeployment",
                "iot:CancelJob",
                "iot:CreateJob",
                "iot:DeleteThingShadow",
                "iot:DescribeJob",
                "iot:DescribeThing",
                "iot:DescribeThingGroup",
                "iot:GetThingShadow",
                "iot:UpdateJob",
                "iot:UpdateThingShadow"
            ],
            "Resource": "*"
        }
    ]
}

```

I create a Role named `SimulatedBBQDeviceInstanceRole` with the above permissions, and assign this the EC2 instance used to simulated the BBQ device.
When the EC2 instance is running I connect to it using `EC2 Instance Connect`.

### Install Requirements

First of all Java need to be installed on the instance, since i use a Amazon Linux 2023 based instance Java is installed with command.

``` bash
sudo dnf install java-11-amazon-corretto -y

java -version
```

Next Python and PIP are needed. Amazon Linux 2023 should come with Python 3.9 installed, verify this by running `which python3`. If Python should not be installed run:

``` bash
sudo dnf install python3.9 -y
```

Next install PIP for the corresponding Python version

``` bash
sudo dnf install python3.9-pip -y
```

Finally install AWS IoT SDK fpr Python, but before you do this ensure you have the latest version of AWS CLI installed, [follow this guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).

``` bash
sudo pip3 install awsiotsdk --user
```

### Setup GreenGrass Core

To setup GreenGrass Core on the device navigate back to the IoT Console, select `Greengrass` and `Core devices` then click on `Set up one Greengrass core device` I give it a name and don't assign any Thing group.
The rest of the steps are in the wizard but summarized we should:

1: Download the installer

``` bash
curl -s https://d2s8p88vqu9w66.cloudfront.net/releases/greengrass-nucleus-latest.zip > greengrass-nucleus-latest.zip && unzip greengrass-nucleus-latest.zip -d GreengrassInstaller
```

2: Run installer

``` bash
sudo -E java -Droot="/greengrass/v2" -Dlog.store=FILE -jar ./GreengrassInstaller/lib/Greengrass.jar --aws-region eu-west-1 --thing-name SimulatedBBQDeviceOne  --component-default-user ggc_user:ggc_group --provision true --setup-system-service true --deploy-dev-tools true
```

There should be a print in the end indicating success `Successfully set up Nucleus as a system service` and the device should now be visible in the IoT Core Console.

### Install components

Now let's install the component that is needed. Navigate to `Deployments` in the GreenGrass section of IoT Core console, and locate the Deployment for the Simulated device.

![Image showing install component](https://jimmydqv.com/assets/img/post-bbq-saas-part-1/deploy-version.png)

From the Actions menu select `Revise` and the `Revise Deployment` button.

Leave target as is and just click next.

Select the HelloBBQ component, LogManager, CloudWatch, and Nucleus, and click next.

![Image showing install component](https://jimmydqv.com/assets/img/post-bbq-saas-part-1/install-component-2.png)

Next there is a need to configure `LogManager` and `Nucleus` components.

![Image showing configure component](https://jimmydqv.com/assets/img/post-bbq-saas-part-1/configure-components.png)

In the Configuration screen select LogManager and click Configure, add the below JSON to the merge config.

``` json
{
    "logsUploaderConfiguration": {
        "systemLogsConfiguration": {
            "uploadToCloudWatch": "true"
        },
        "componentLogsConfigurationMap": {
            "com.example.hellobbq": {}
        }
    }
}
```

Now repeat the process for `Nucleus` where we need to set `interpolateComponentConfiguration` otherwise `{iot:thingName}` will not be inflated to a proper value.

``` json
{
  "reset": [],
  "merge": {
    "reset": [],
    "merge": {
      "interpolateComponentConfiguration": "true"
    }
  }
}
```

Now just click next in rest of the dialog and confirm the deployment to start the process.

To verify that everything now is running navigate to Core Devices, select the simulated device, and check that all components are running.

![Image showing running components](https://jimmydqv.com/assets/img/post-bbq-saas-part-1/running-components.png)

## Verify data

To verify that data is sent as expected open the MQTT test client in the console and create a subscription to `e/#`. Data should start flowing in the format we defined in the code.

![Image showing iot data](https://jimmydqv.com/assets/img/post-bbq-saas-part-1/verify-iot-data.png)

## Cloudwatch Logs

To check the logs from the device, navigate to CloudWatch Logs, there should a Log group with the component name e.g. `/aws/greengrass/UserComponent/eu-west-1/com.example.hellobbq`

## Device Logs

It's also possible to get the logs on the device it self. Connect to the instance, using Instance Connect. Logs can be found in folder `/greengrass/v2/logs`

## Final Words

This was the first part in building a connected BBQ as a SaaS solution. We looked at creating a GreenGrass Core component and device running as a simulation on an EC2 instance.

Check out [My serverless Handbook](https://serverless-handbook.com) for some of the concepts mentioned in this post.

Don't forget to follow me on [LinkedIn](https://www.linkedin.com/in/dahlqvistjimmy/) and [X](https://x.com/jimmydahlqvist) for more content, and read rest of my [Blogs](https://jimmydqv.com)

As Werner says! Now Go Build!