Building a serverless connected BBQ as SaaS - Part 1
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.
gdk component init -l python -t HelloWorld -n HelloBBQ
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.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"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"
}
e/{THING_NAME}/data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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()
{artifacts:decompressedPath}
, full reference can be found here{iot:thingName}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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}"
1
gdk component build
greengrass-build/COMPONENT_NAME/VERSION/
1
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
Greengrass devices
and select Components
, click on Create Component
button to the right.Create Component

t3.micro
instance that I will be using.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
{
"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": "*"
}
]
}
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
.1
2
3
sudo dnf install java-11-amazon-corretto -y
java -version
which python3
. If Python should not be installed run:1
sudo dnf install python3.9 -y
1
sudo dnf install python3.9-pip -y
1
sudo pip3 install awsiotsdk --user
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
curl -s https://d2s8p88vqu9w66.cloudfront.net/releases/greengrass-nucleus-latest.zip > greengrass-nucleus-latest.zip && unzip greengrass-nucleus-latest.zip -d GreengrassInstaller
1
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
Successfully set up Nucleus as a system service
and the device should now be visible in the IoT Core Console.Deployments
in the GreenGrass section of IoT Core console, and locate the Deployment for the Simulated device.
Revise
and the Revise Deployment
button.
LogManager
and Nucleus
components.
1
2
3
4
5
6
7
8
9
10
{
"logsUploaderConfiguration": {
"systemLogsConfiguration": {
"uploadToCloudWatch": "true"
},
"componentLogsConfigurationMap": {
"com.example.hellobbq": {}
}
}
}
Nucleus
where we need to set interpolateComponentConfiguration
otherwise {iot:thingName}
will not be inflated to a proper value.1
2
3
4
5
6
7
8
9
{
"reset": [],
"merge": {
"reset": [],
"merge": {
"interpolateComponentConfiguration": "true"
}
}
}

e/#
. Data should start flowing in the format we defined in the code.
/aws/greengrass/UserComponent/eu-west-1/com.example.hellobbq
/greengrass/v2/logs